Мішок з піском
Під час роботи на високому якість Блог Posts®, я натрапив на якому - то оптимізатор поведінки я знайшов дійсно сказ цікавого. У мене не одразу є пояснення, принаймні, не одне, з чим я задоволений, тому я вкладаю його на випадок, якщо хтось розумний з’явиться.
Якщо ви хочете слідувати далі, ви можете захопити тут версію 2013-го дамп-файлу даних про переповнення стека . Я використовую таблицю коментарів з одним додатковим індексом.
CREATE INDEX [ix_ennui] ON [dbo].[Comments] ( [UserId], [Score] DESC );
Запит перший
Коли я так запитую таблицю, я отримую незвичайний план запитів .
WITH x
AS
(
SELECT TOP 101
c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
ORDER BY c.Score DESC
)
SELECT *
FROM x
WHERE x.Score >= 500;
Присудок SARGable на бал не засувається всередину CTE. Це в операторі фільтру набагато пізніше в плані.
Що мені здається дивним, оскільки це ORDER BY
знаходиться в тому ж стовпці, що і фільтр.
Запит два
Якщо я поміняю запит, він все-таки висувається.
WITH x
AS
(
SELECT c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
)
SELECT TOP 101 *
FROM x
WHERE x.Score >= 500
ORDER BY x.Score DESC;
План запитів теж змінюється і працює набагато швидше, без розливу на диск. Вони обидва дають однакові результати, при цьому присудок при некластеризованому скануванні індексів.
Запит три
Це еквівалент написання запиту так:
SELECT TOP 101
c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
WHERE c.Score >= 500
ORDER BY c.Score DESC;
Запит четвертий
Використання похідної таблиці отримує той же «поганий» план запитів, що і початковий CTE-запит
SELECT *
FROM ( SELECT TOP 101
c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
ORDER BY c.Score DESC ) AS x
WHERE x.Score >= 500;
Все стає ще дивнішим, коли ...
Я змінюю запит, щоб замовити дані у зростанні, а фільтр - <=
.
Щоб не ставити це питання надто довго, я збираюся все скласти разом.
Запити
--Derived table
SELECT *
FROM ( SELECT TOP 101
c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
ORDER BY c.Score ASC ) AS x
WHERE x.Score <= 500;
--TOP inside CTE
WITH x
AS
(
SELECT TOP 101
c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
ORDER BY c.Score ASC
)
SELECT *
FROM x
WHERE x.Score <= 500;
--Written normally
SELECT TOP 101
c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
WHERE c.Score <= 500
ORDER BY c.Score ASC;
--TOP outside CTE
WITH x
AS
(
SELECT c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
)
SELECT TOP 101 *
FROM x
WHERE x.Score <= 500
ORDER BY x.Score ASC;
Плани
Зауважте, що жоден із цих запитів не користується перевагою некластеризованого індексу - єдине, що тут змінюється, - це позиція оператора фільтра. Ні в якому разі предикат не підштовхується до доступу до індексу.
З'являється питання!
Чи є причина, що предикат SARGable може бути висунутий в одних сценаріях, а не в інших? Відмінності в запитах, відсортованих у порядку зменшення, цікаві, але відмінності між тими і тими, що є висхідними, химерними.
Для всіх, хто цікавиться, ось плани з лише індексом на Score
: