У меня есть постоянный вычисляемый столбец в таблице, который просто состоит из сцепленных столбцов, например
CREATE TABLE dbo.T
(
ID INT IDENTITY(1, 1) NOT NULL CONSTRAINT PK_T_ID PRIMARY KEY,
A VARCHAR(20) NOT NULL,
B VARCHAR(20) NOT NULL,
C VARCHAR(20) NOT NULL,
D DATE NULL,
E VARCHAR(20) NULL,
Comp AS A + '-' + B + '-' + C PERSISTED NOT NULL
);
Это Comp
не уникально, и D является действительной датой каждой комбинации A, B, C
, поэтому я использую следующий запрос, чтобы получить конечную дату для каждой A, B, C
(в основном, следующую начальную дату для того же значения Comp):
SELECT t1.ID,
t1.Comp,
t1.D,
D2 = ( SELECT TOP 1 t2.D
FROM dbo.T t2
WHERE t2.Comp = t1.Comp
AND t2.D > t1.D
ORDER BY t2.D
)
FROM dbo.T t1
WHERE t1.D IS NOT NULL -- DON'T CARE ABOUT INACTIVE RECORDS
ORDER BY t1.Comp;
Затем я добавил индекс в вычисляемый столбец, чтобы помочь в этом запросе (а также других):
CREATE NONCLUSTERED INDEX IX_T_Comp_D ON dbo.T (Comp, D) WHERE D IS NOT NULL;
Однако план запроса меня удивил. Я бы подумал, что, поскольку у меня есть предложение where, в котором указано, что D IS NOT NULL
я сортирую Comp
, а не ссылаюсь ни на один столбец за пределами индекса, индекс для вычисляемого столбца можно использовать для сканирования t1 и t2, но я увидел кластеризованный индекс сканирования.
Поэтому я заставил использовать этот индекс, чтобы увидеть, дает ли он лучший план:
SELECT t1.ID,
t1.Comp,
t1.D,
D2 = ( SELECT TOP 1 t2.D
FROM dbo.T t2
WHERE t2.Comp = t1.Comp
AND t2.D > t1.D
ORDER BY t2.D
)
FROM dbo.T t1 WITH (INDEX (IX_T_Comp_D))
WHERE t1.D IS NOT NULL
ORDER BY t1.Comp;
Который дал этот план
Это показывает, что используется поиск ключа, подробности которого:
Теперь согласно документации SQL-сервера:
Вы можете создать индекс для вычисляемого столбца, который определен с помощью детерминированного, но неточного выражения, если столбец помечен как PERSISTED в операторе CREATE TABLE или ALTER TABLE. Это означает, что компонент Database Engine сохраняет вычисленные значения в таблице и обновляет их при обновлении любых других столбцов, от которых зависит вычисляемый столбец. Компонент Database Engine использует эти постоянные значения при создании индекса для столбца и при ссылке на индекс в запросе. Этот параметр позволяет создать индекс для вычисляемого столбца, когда компонент Database Engine не может с точностью доказать, является ли функция, которая возвращает выражения вычисляемого столбца, в частности, функция CLR, созданная в .NET Framework, детерминистической и точной.
Поэтому, если, как говорят документы, « компонент Database Engine хранит вычисленные значения в таблице» , а значение также сохраняется в моем индексе, почему для поиска A, B и C требуется поиск ключей, если на них нет ссылок в запрос вообще? Я предполагаю, что они используются для вычисления Comp, но почему? Кроме того, почему запрос может использовать индекс включен t2
, но не включен t1
?
NB Я пометил SQL Server 2008, потому что это версия, на которой моя основная проблема, но я также получаю такое же поведение в 2012 году.
FOJNtoLSJNandLASJN
), которое приводит к тому, что все работает не так, как мы надеемся, и оставляет ненужную информацию (BaseRow / Checksums), которая полезна в некоторых типах планов (например, курсоры), но не нужна здесь.Chk
это контрольная сумма! Спасибо, я не был уверен в этом. Первоначально я думал, что это может быть связано с проверочными ограничениями.Хотя это может быть несколько случайным из-за искусственного характера ваших тестовых данных, но, как вы упомянули SQL 2012, я попытался переписать:
Это дало хороший недорогой план с использованием вашего индекса и со значительно меньшим чтением, чем другие варианты (и те же результаты для ваших тестовых данных).
Я подозреваю, что ваши реальные данные более сложны, поэтому могут быть некоторые сценарии, когда этот запрос ведет себя семантически иначе, чем ваш, но иногда он показывает, что новые функции могут иметь реальное значение.
Я экспериментировал с некоторыми более разнообразными данными и обнаружил, что некоторые сценарии совпадают, а некоторые нет:
источник
comp
это не вычисляемый столбец, вы не видите сортировку.LEAD
функция работала именно так, как я хотел бы на своем локальном экземпляре 2012 Express. К сожалению, это незначительное неудобство для меня еще не считалось достаточной причиной для обновления производственных серверов ...Когда я пытался выполнить те же действия, я получал другие результаты. Во-первых, мой план выполнения для таблицы без индексов выглядит следующим образом:
Как видно из сканирования кластерного индекса (t2), предикат используется для определения необходимых строк, которые должны быть возвращены (из-за условия):
Когда индекс был добавлен, независимо от того, был ли он определен оператором WITH или нет, план выполнения стал следующим:
Как мы видим, сканирование кластеризованного индекса заменено сканированием индекса. Как мы видели выше, SQL Server использует исходные столбцы вычисляемого столбца для выполнения сопоставления вложенного запроса. Во время сканирования кластерного индекса все эти значения могут быть получены одновременно (никаких дополнительных операций не требуется). Когда индекс был добавлен, фильтрация необходимых строк из таблицы (в главном выборе) выполняется в соответствии с индексом, но значения исходных столбцов для вычисляемого столбца
comp
все еще необходимо получить (последняя операция Nested Loop) ,Из-за этого используется операция Key Lookup - для получения данных исходных столбцов вычисляемого.
PS Похоже, ошибка в SQL Server.
источник