У мене на столі зберігається обчислюваний стовпчик, який просто складається з з'єднаних стовпців, наприклад
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;
План запитів, однак, мене здивував. Я б подумав, що оскільки у мене є пункт де зазначено, що 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-сервер:
Ви можете створити індекс на обчисленому стовпчику, який визначений детермінованим, але неточним виразом, якщо стовпець позначений ПЕРСИСТИЧНО в операторі CREATE TABLE або ALTER TABLE. Це означає, що Database Engine зберігає обчислені значення в таблиці та оновлює їх, коли оновлюються будь-які інші стовпці, від яких залежить обчислений стовпець. База даних двигунів використовує ці збережені значення, коли створює індекс у стовпці та коли на індекс посилається запит. Цей параметр дозволяє створити індекс на обчисленому стовпчику, коли Database Engine не може з точністю довести, чи функція, яка повертає обчислювані вирази стовпців, зокрема функція CLR, створена в .NET Framework, є детермінованою і точною.
Отже, як кажуть документи "Database Engine зберігає обчислювані значення в таблиці" , а значення також зберігається в моєму індексі, чому для пошуку A, B і C потрібен пошук ключа, коли вони не посилаються на запит взагалі? Я припускаю, що вони використовуються для обчислення Comp, але чому? Крім того, чому запит може використовувати індекс на t2
, а не на t1
?
NB Я позначив SQL Server 2008, тому що це версія, в якій головна моя проблема, але я також отримую таку ж поведінку в 2012 році.