принуждать
Работая над Top Quality Blog 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 для Score не помещается в 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;
Query Four
При использовании производной таблицы получается тот же «плохой» план запроса, что и в исходном запросе 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
: