WHERE 절이“포함 된”열의 이점을 얻는 이유는 무엇입니까?


12

이 답변 에 따르면 , 제한하는 데 사용되는 열 위에 인덱스가 작성되지 않으면 쿼리는 인덱스의 이점을 얻지 못합니다.

나는이 정의를 가지고있다 :

CREATE TABLE [dbo].[JobItems] (
    [ItemId]             UNIQUEIDENTIFIER NOT NULL,
    [ItemState]          INT              NOT NULL,
    [ItemPriority]       INT NOT NULL,
    [CreationTime]       DATETIME         NULL DEFAULT GETUTCDATE(),
    [LastAccessTime]     DATETIME         NULL DEFAULT GETUTCDATE(),
     -- other columns
 );

 CREATE UNIQUE CLUSTERED INDEX [JobItemsIndex]
    ON [dbo].[JobItems]([ItemId] ASC);
 GO

CREATE INDEX [GetItemToProcessIndex]
    ON [dbo].[JobItems]([ItemState], [ItemPriority], [CreationTime])
    INCLUDE (LastAccessTime);
GO

그리고이 쿼리 :

UPDATE TOP (150) JobItems 
SET ItemState = 17 
WHERE 
    ItemState IN (3, 9, 10)
    AND LastAccessTime < DATEADD (day, -2, GETUTCDATE()) 
    AND CreationTime < DATEADD (day, -2, GETUTCDATE());

실제 계획을 검토 한 결과, 인덱스의 일부가 아닌 인덱스에 "포함"되어 있어도 WHERE검색 LastAccessTime할 추가 "책갈피 검색"이없는 것과 마찬가지로 술어를 사용하여 하나의 인덱스 탐색 만 수행 할 수 있습니다.

이 동작이 열이 "포함"이 아니라 인덱스의 일부 여야한다는 규칙과 모순되는 것처럼 보입니다.

내가 올바른 행동을 하는가? WHERE포함 된 열의 이점이 있는지 또는 해당 열이 인덱스의 일부 여야 하는지 어떻게 미리 알 수 있습니까?


그것은 여전히 ItemState가치를 기반으로 탐색 할 수 있지만, 다음과 같이 인덱스가 구조화 된 것처럼 탐색은 효율적이지 않을 것입니다(ItemState, CreationTime, LastAccessTime)
Mark Sinkinson

1
@MarkSinkinson 또는 그냥(ItemState, CreationTime) INCLUDE (LastAccessTime)
ypercubeᵀᴹ

@sharptooth 당신이 말한 답변을하지 않습니다 ( "쿼리를 제한하는 데 사용되는 열 위에 인덱스가 작성되지 않는 한 인덱스의 이점을 얻지 못합니다"). 인덱스 온 (a,b)은 쿼리에 가장 적합하지 않으며 SELECT a FROM t WHERE b=5;인덱스 온 (b) INCLUDE (a)은 훨씬 낫습니다.
ypercubeᵀᴹ

답변:


9

당신의 술어는 당신의 찾는 술어와 다릅니다.

Seek Predicate는 인덱스에서 정렬 된 데이터를 검색하는 데 사용됩니다. 이 경우, 관심이있는 각 ItemState마다 하나씩 세 번의 탐색을 수행합니다. 그 외에도 데이터는 ItemPriority 순서이므로 더 이상 "탐색"조작을 수행 할 수 없습니다.

그러나 데이터가 반환되기 전에 Predicate를 사용하여 모든 행을 확인합니다.이를 Residual Predicate라고합니다. Seek Predicate의 결과에 따라 수행됩니다.

포함 된 열은 순서화 된 데이터의 일부가 아니지만 추가 조회를 수행하지 않고 잔여 술어를 만족시키는 데 사용될 수 있습니다.

Sargability와 관련하여 내가 작성한 자료를 볼 수 있습니다. SQLBits, 특히 http://bit.ly/Sargability 에서 세션을 확인하십시오.

편집 : 잔차의 영향을 더 잘 나타내려면 문서화되지 않은을 사용하여 쿼리를 실행하면 OPTION (QUERYTRACEON 9130)잔차가 별도의 필터 연산자 (잔차가 찾기 연산자로 이동되기 전에 계획의 이전 버전 임)로 분리됩니다. 필터에 남아있는 행 수로 비효율적 인 탐색의 영향을 명확하게 보여줍니다.

ItemState의 IN 절로 인해 왼쪽으로 전달되는 데이터는 실제로 ItemPriority 순서가 아니라 ItemState 순서로되어 있습니다. ItemState의 복합 인덱스 다음에 날짜 중 하나 (예 : (ItemState, LastAccessTime))를 사용하면 세 개의 Seeks (Seek 술어가 하나의 Seek 연산자 내에서 세 개의 탐색을 표시 함)를 두 레벨로 비교할 수 있습니다. 여전히 ItemState 순서로되어 있습니다 (예 : ItemState = 3 및 LastAccessTime이 다른 것보다 작음, ItemState = 9 및 LastAccessTime이 다른 것보다 작음, ItemState = 10 및 LastAccessTime보다 작음).

(ItemState, LastAccesTime, CreationTime)에 대한 인덱스는 (ItemState, LastAccessTime)에 대한 인덱스보다 더 유용하지 않습니다. CreationTime 수준은 검색이 범위가 아닌 특정 ItemState 및 LastAccessTime 조합에 대한 경우에만 유용하기 때문입니다. F로 시작하는성에 관심이 있다면 전화 번호부가 FirstName 순서가 아닌 것과 같습니다.

복합 인덱스를 원하지만 이전 컬럼을 사용하는 방식으로 인해 Seek Predicates에서 이후 컬럼을 사용할 수없는 경우, 컬럼을 더 적게 차지하는 포함 컬럼으로 사용할 수도 있습니다. index (상위 레벨이 아닌 인덱스의 리프 레벨에만 저장되기 때문에)는 여전히 조회를 피하고 Residual 술어에서 사용할 수 있습니다.

Residual Predicate라는 용어에 따르면-이것은 Seek의이 속성에 대한 내 자신의 용어입니다. 병합 조인은이를 명시 적으로 해당 레지 듀얼 술어라고 부르며, 해시 매치는이를 하나의 프로브 레지 듀얼 (해시와 일치하는 경우 TSA에서 얻을 수 있음)이라고합니다. 그러나 Seek에서 그들은 단지 Predicate라고 부르는데, 그것은 그것보다 덜 나쁘게 보입니다.


3

where 절이 켜져 있으므로 GetItemToProcessIndex를 완전히 검색 할 수 없습니다 ItemState + LastAccessTime + CreationTime. 인덱싱 된 열과 where 절이 완벽하게 일치하지 않습니다.

에 커버링 인덱스를 생성하면 ItemState + LastAccessTime + CreationTimeGetItemToProcessIndex에서 얻을 수있는 일치 항목마다 기본 키 (ItemId)의 값도 얻습니다. 두 번째 날짜가 일치해야합니다.

이것으로 페이지의 행 위치로 이동하여 업데이트해야합니다.

현재 인덱스를 사용하면 서버가 원하는 ItemState가있는 행을 찾는 데 도움이 될 수 있지만 LastAccessTime + CreationTime에서 올바른 일치 항목을 찾으려면 인덱스에서 모든 행을 읽어야합니다. 날짜 술어와 일치하는 세트의 크기 및 제외해야 할 항목에 따라 ItemState와 두 번째 열 (첫 번째 색인 날짜)을 찾는 3 개의 열에서만 완벽하게 포함하는 인덱스보다 훨씬 많은 IO가 발생할 수 있습니다. . 그러나 색인의 두 번째 날짜가 포함될 수 있습니다. 추가 열은 4 열로 괜찮을 수 있지만이 열 사이에 색인을 생성해서는 안됩니다 (추가 열에 대한 rob의 답변 참조).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.