Почему мое предложение 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)
Марк Синкинсон

1
@MarkSinkinson или просто(ItemState, CreationTime) INCLUDE (LastAccessTime)
ypercubeᵀᴹ

@sharptooth связанный ответ, который у вас есть, этого не говорит («если индекс не построен по столбцам, которые используются для ограничения запроса, вы не извлечете выгоду из индекса»). В нем говорится, что индекс для (a,b)не является лучшим для запроса с, SELECT a FROM t WHERE b=5;и что индекс на (b) INCLUDE (a)гораздо лучше.
ypercubeᵀᴹ

Ответы:


9

Ваш предикат отличается от вашего предиката поиска.

Предикат поиска используется для поиска упорядоченных данных в индексе. В этом случае он будет выполнять три поиска, по одному для каждого ItemState, в котором вы заинтересованы. Кроме того, данные располагаются в порядке ItemPriority, поэтому дальнейшая операция «Поиск» невозможна.

Но перед возвратом данных он проверяет каждую строку, используя Предикат, который я называю Остаточным Предикатом. Это сделано по результатам поиска предиката.

Любой включенный столбец не является частью упорядоченных данных, но может использоваться для удовлетворения остаточного предиката без необходимости дополнительного поиска.

Вы можете увидеть материал, который я написал по этому поводу вокруг Sargability. В частности, проверьте сеанс в SQLBits, по адресу http://bit.ly/Sargability

Изменить: чтобы лучше показать влияние остатков, запустите запрос, используя недокументированный документ OPTION (QUERYTRACEON 9130), который разделит остаток на отдельный оператор фильтра (который на самом деле является более ранней версией плана до того, как остаток будет перемещен в оператор поиска). Он ясно показывает влияние неэффективного поиска по количеству строк, переданных в фильтр.

Также стоит отметить, что из-за предложения IN в ItemState передаваемые данные в действительности находятся в порядке ItemState, а не в ItemPriority. Составной индекс ItemState, за которым следует одна из дат (например, (ItemState, LastAccessTime)), можно использовать для трех поисков (обратите внимание, что предикат поиска показывает три поиска в одном операторе поиска), каждый для двух уровней, что приводит к получению данных, которые по-прежнему в порядке ItemState (например, ItemState = 3 и LastAccessTime меньше чем-то, затем ItemState = 9 и LastAccessTime меньше чем-то, а затем ItemState = 10 и LastAccessTime меньше чем-то).

Индекс на (ItemState, LastAccesTime, CreationTime) будет не более полезным, чем индекс на (ItemState, LastAccessTime), поскольку уровень CreationTime полезен только в том случае, если ваш Seek предназначен для определенной комбинации ItemState и LastAccessTime, а не для диапазона. Например, телефонная книга не в порядке FirstName, если вас интересуют фамилии, начинающиеся с F.

Если вам нужен составной индекс, но вы никогда не сможете использовать более поздние столбцы в поиске предикатов из-за того, как вы используете более ранние столбцы, то вы можете также использовать их как включенные столбцы, где они занимают меньше места в index (поскольку они хранятся только на уровне листа индекса, а не на более высоких уровнях), но все же могут избегать поиска и использоваться в остаточных предикатах.

Согласно термину Остаточный предикат - это мой собственный термин для этого свойства поиска. Соединение слиянием в явном виде называет его эквивалентом остаточный предикат, а хэш-совпадение называет его остаточным предикатом (который вы можете получить от TSA, если сопоставите хеш). Но в Поисках они просто называют это Предикатом, что делает его менее плохим, чем есть.


3

GetItemToProcessIndex не полностью доступен для поиска, потому что включено условие where ItemState + LastAccessTime + CreationTime. Индексированные столбцы и предложение where не идеально совпадают.

Если вы создаете индекс покрытия ItemState + LastAccessTime + CreationTimeдля каждого совпадения, получаемого из GetItemToProcessIndex, вы также получаете значение вашего первичного ключа (ItemId). Нужно только убедиться, что вторая дата совпадает.

Это все, что вам нужно, чтобы затем перейти к расположению строки на ее странице и обновить ее.

С вашим текущим индексом он может помочь серверу найти строки с нужным вам ItemState, но ему все равно придется прочитать все из индекса, чтобы найти правильные совпадения в LastAccessTime + CreationTime. В зависимости от предикатов даты и размера соответствующего набора и того, что должно быть исключено, это может привести к намного большему количеству операций ввода-вывода, чем идеально охватывающий индекс только для 3 столбцов, который будет искать ItemState и второй столбец (1-я индексированная дата) , Вторая дата в индексированном может быть включена, хотя. Дополнительные столбцы не должны индексироваться между этими 3, хотя это может быть хорошо, как 4-й столбец (см. Ответ Роба о дополнительных столбцах).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.