IN ()을 사용하여 쿼리 성능 향상


14

다음 SQL 쿼리가 있습니다.

SELECT
  Event.ID,
  Event.IATA,
  Device.Name,
  EventType.Description,
  Event.Data1,
  Event.Data2
  Event.PLCTimeStamp,
  Event.EventTypeID
FROM
  Event
INNER JOIN EventType ON EventType.ID = Event.EventTypeID
INNER JOIN Device ON Device.ID = Event.DeviceID
WHERE
  Event.EventTypeID IN (3, 30, 40, 41, 42, 46, 49, 50)
  AND Event.PLCTimeStamp BETWEEN '2011-01-28' AND '2011-01-29'
  AND Event.IATA LIKE '%0005836217%'
ORDER BY Event.ID;

또한 Event열에 대한 테이블에 인덱스가 TimeStamp있습니다. 나는 이해하기 때문에이 색인은 IN()진술 때문에 사용되지 않는다는 것 입니다. 그래서 내 질문은이 특정 IN()문에 대한 색인을 만들어이 쿼리 속도를 높이는 방법이 있습니까?

또한에 Event.EventTypeID IN (2, 5, 7, 8, 9, 14)대한 필터로 추가 를 시도했지만 TimeStamp실행 계획을 볼 때이 인덱스를 사용하지 않는 것 같습니다. 이에 대한 제안이나 통찰력은 크게 감사하겠습니다.

아래는 그래픽 계획입니다.

실행 계획

그리고 여기 에 .sqlplan 파일에 대한 링크가 있습니다 .


실행 계획도 살펴볼 수 있습니까? :)
dezso

1
그리고 실제 실행 계획 (추정되지 않음)을 .sqlplan 확장자로 게시하십시오. 대부분의 사람들은 그래픽 계획의 스크린 샷을 게시하기를 원하지만 그다지 유용하지 않습니다.
Aaron Bertrand

확인 실행 계획을 추가하고 SQL 쿼리를 업데이트했습니다.
SandersKY

@SandersKY 질문과 관련된 모든 것을 동일한 사이트에 유지하려면 .sqlplan 파일을 인라인하는 것이 가장 좋습니다.
Trygve Laugstøl

1
@trygvis-게시물의 길이 제한으로 인해 종종 불가능합니다. 부끄러운 스택 교환은 내부적으로 첨부 파일 호스팅을 지원하지 않습니다.
Martin Smith

답변:


18

다음과 같은 일반적인 형식의 테이블이 제공됩니다.

CREATE TABLE Device 
(
    ID integer PRIMARY KEY
);

CREATE TABLE EventType
(
    ID integer PRIMARY KEY, 
    Name nvarchar(50) NOT NULL
);

CREATE TABLE [Event]
(
    ID integer PRIMARY KEY, 
    [TimeStamp] datetime NOT NULL, 
    EventTypeID integer NOT NULL REFERENCES EventType, 
    DeviceID integer NOT NULL REFERENCES Device
);

다음 색인이 유용합니다.

CREATE INDEX f1 
ON [Event] ([TimeStamp], EventTypeID) 
INCLUDE (DeviceID)
WHERE EventTypeID IN (2, 5, 7, 8, 9, 14);

쿼리의 경우 :

SELECT
  [Event].ID,
  [Event].[TimeStamp],
  EventType.Name,
  Device.ID
FROM
  [Event]
INNER JOIN EventType ON EventType.ID = [Event].EventTypeID
INNER JOIN Device ON Device.ID = [Event].DeviceID
WHERE
  [Event].[TimeStamp] BETWEEN '2011-01-28' AND '2011-01-29'
  AND Event.EventTypeID IN (2, 5, 7, 8, 9, 14);

필터는 AND절의 요구 사항을 충족하며 , 인덱스의 첫 번째 키 [TimeStamp]는 필터링 된 검색을 허용 EventTypeIDs하고 DeviceID열을 포함하여 인덱스를 덮습니다 ( 테이블에 DeviceID조인하는 데 필요하기 때문에 Device).

완성 된 계획

인덱스의 두 번째 키 EventTypeID는 꼭 필요한 것은 아닙니다 ( INCLUDEd열일 수도 있음 ). 여기에 언급 된 이유로 에 포함 시켰습니다 . 일반적으로 필터링 된 인덱스 절의 열을 사람들에게 권합니다 .INCLUDEWHERE


질문의 업데이트 된 쿼리 및 실행 계획을 기반으로, EventTypeIDsAaron이 자신의 답변에서 언급 한 것처럼 필터링 된 목록 이 정적 인 경우가 아니라면 SSMS가 제안한보다 일반적인 색인이 여기에서 더 나은 선택 일 것입니다.

CREATE TABLE Device 
(
    ID integer PRIMARY KEY,
    Name nvarchar(50) NOT NULL UNIQUE
);

CREATE TABLE EventType
(
    ID integer PRIMARY KEY, 
    Name nvarchar(20) NOT NULL UNIQUE,
    [Description] nvarchar(100) NOT NULL
);

CREATE TABLE [Event]
(
    ID integer PRIMARY KEY, 
    PLCTimeStamp datetime NOT NULL,
    EventTypeID integer NOT NULL REFERENCES EventType, 
    DeviceID integer NOT NULL REFERENCES Device,
    IATA varchar(50) NOT NULL,
    Data1 integer NULL,
    Data2 integer NULL,
);

제안 된 색인 (적절한 경우 고유 한 것으로 선언) :

CREATE UNIQUE INDEX uq1
ON [Event]
    (EventTypeID, PLCTimeStamp)
INCLUDE 
    (DeviceID, IATA, Data1, Data2, ID);

실행 계획의 카디널리티 정보 (문서화되지 않은 구문, 프로덕션 시스템에서는 사용하지 않음) :

UPDATE STATISTICS dbo.Event WITH ROWCOUNT = 4042700, PAGECOUNT = 400000;
UPDATE STATISTICS dbo.EventType WITH ROWCOUNT = 22, PAGECOUNT = 1;
UPDATE STATISTICS dbo.Device WITH ROWCOUNT = 2806, PAGECOUNT = 28;

업데이트 된 쿼리 ( 테이블 의 IN목록을 반복하면 EventType이 특정 경우 옵티 마이저에 도움이 됨) :

SELECT
  Event.ID,
  Event.IATA,
  Device.Name,
  EventType.Description,
  Event.Data1,
  Event.Data2,
  Event.PLCTimeStamp,
  Event.EventTypeID
FROM
  Event
INNER JOIN EventType ON EventType.ID = Event.EventTypeID
INNER JOIN Device ON Device.ID = Event.DeviceID
WHERE
  Event.EventTypeID IN (3, 30, 40, 41, 42, 46, 49, 50)
  AND EventType.ID IN (3, 30, 40, 41, 42, 46, 49, 50)
  AND Event.PLCTimeStamp BETWEEN '2011-01-28' AND '2011-01-29'
  AND Event.IATA LIKE '%0005836217%'
ORDER BY Event.ID;

예상 실행 계획 :

두 번째 계획

내가 추측 한 통계를 사용하고 있기 때문에 계획이 다를 수 있습니다. 일반적인 요점은 옵티 마이저에게 가능한 많은 정보를 제공하고 4 백만 행 [Event]테이블 에 효율적인 액세스 방법 (인덱스)을 제공하는 것 입니다.


8

비용의 대부분은 클러스터형 인덱스 스캔이며,이 테이블이 실제로 넓거나 출력에 해당 열이 모두 필요하지 않은 경우 SQL Server는 현재 시나리오에서 변경되지 않은 최적의 경로라고 생각합니다 . 범위 검색 (CI 검색으로 레이블 지정)을 사용하여 관심있는 행 범위를 좁히지 만 출력으로 인해 필터링 된 색인을 생성 한 경우에도 여전히 조회 또는 CI 스캔이 필요합니다. 이 범위를 대상으로하며,이 경우에도 CI 스캔은 여전히 ​​가장 저렴합니다 (또는 최소한 SQL Server는이를 추정합니다).

실행 계획에 따르면이 인덱스가 유용 할 것입니다.

CREATE NONCLUSTERED INDEX ix_EventTypeID_PLCTimeStamp_WithIncludes
  ON [dbo].[Event] ([EventTypeID],[PLCTimeStamp])
  INCLUDE ([ID],[DeviceID],[Data1],[Data2],[IATA]);

데이터 왜곡에 따라 다른 방법으로 더 나을 수 있습니다.

CREATE NONCLUSTERED INDEX ix_PLCTimeStamp_EventTypeID_WithIncludes
  ON [dbo].[Event] ([PLCTimeStamp],[EventTypeID])
  INCLUDE ([ID],[DeviceID],[Data1],[Data2],[IATA]);

그러나 나는 둘 중 어느 것이 더 나은지 확인하기 위해 테스트 할 것입니다. 그 인덱스 중 하나와 현재 가지고있는 것과의 차이가 미미할 수도 있고 (알려야 할 변수가 너무 많음) 추가 사항을 고려해야합니다. 인덱스에는 추가 유지 관리가 필요하며 이는 DML 작업 (삽입 / 업데이트 / 삭제)에 현저하게 영향을 줄 수 있습니다. @SQLKiwi가 제안한대로이 인덱스에 필터 기준을 포함하는 것을 고려할 수도 있지만, 자주 검색하는 EventTypeID 값 세트 인 경우에만 가능합니다. 시간이 지남에 따라 설정이 변경되면 필터링 된 인덱스는이 특정 쿼리에만 유용합니다.

행 수가 적 으면 성능이 현재 얼마나 나쁜지 궁금합니다. 이 쿼리는 3 개의 행을 반환하지만 거부 된 행 수에 대한 표시는 없습니다. 테이블에 몇 개의 행이 있습니까?


4

SQL Server 2008 R2가 실제로 실행 계획을 실행할 때 인덱스 제안을했다는 것을 알게되었습니다. 이 제안 된 인덱스는 쿼리를 약 90 % 더 빠르게 실행합니다.

제안한 색인은 다음과 같습니다.

CREATE NONCLUSTERED INDEX [INDEX_spBagSearch] ON [dbo].[Event] 
(
    [EventTypeID] ASC,
    [PLCTimeStamp] ASC
)
INCLUDE ( [ID],
[DeviceID],
[Data1],
[Data2],
[IATA]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.