Worek z piaskiem
Pracując na Top Quality Blog Posts® natknąłem jakiegoś zachowania optymalizatora znalazłem naprawdę irytujące interesujące. Nie mam od razu wyjaśnienia, przynajmniej takiego, z którego jestem zadowolony, więc umieszczam je tutaj na wypadek, gdyby pojawił się ktoś inteligentny.
Jeśli chcesz śledzić, możesz pobrać zrzut zrzutu danych przepełnienia stosu z 2013 r . Tutaj . Korzystam z tabeli Komentarze z jednym dodatkowym indeksem.
CREATE INDEX [ix_ennui] ON [dbo].[Comments] ( [UserId], [Score] DESC );
Zapytanie pierwsze
Kiedy przeszukuję tabelę w ten sposób, otrzymuję dziwny plan zapytań .
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;
Predykat SARGable dotyczący wyniku nie jest wpychany do CTE. Jest w operatorze filtrów znacznie później w planie.
Co uważam za dziwne, ponieważ ORDER BY
jest w tej samej kolumnie co filtr.
Pytanie drugie
Jeśli zmienię zapytanie, zostanie ono wypchnięte.
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;
Plan zapytań również się zmienia i działa znacznie szybciej, bez rozlewania się na dysk. Oba dają takie same wyniki, z predykatem podczas skanowania indeksu nieklastrowanego.
Pytanie trzecie
Jest to odpowiednik napisania zapytania w ten sposób:
SELECT TOP 101
c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
WHERE c.Score >= 500
ORDER BY c.Score DESC;
Pytanie czwarte
Użycie tabeli pochodnej powoduje powstanie tego samego „złego” planu zapytań, co wstępne zapytanie 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;
Sprawa staje się jeszcze dziwniejsza, gdy ...
Zmieniam zapytanie, aby uporządkować dane rosnąco, a filtr na <=
.
Aby uniknąć przedłużania się tego pytania, złożę wszystko.
Zapytania
--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;
Plany
Zauważ, że żadne z tych zapytań nie korzysta z indeksu nieklastrowanego - jedyne, co się tutaj zmienia, to pozycja operatora filtru. W żadnym wypadku predykat nie jest przekazywany do dostępu do indeksu.
Pojawia się pytanie!
Czy istnieje powód, dla którego predykat SARGable może być wypychany w niektórych scenariuszach, a nie w innych? Różnice w zapytaniach posortowanych w porządku malejącym są interesujące, ale różnice między tymi a rosnącymi są dziwne.
Dla wszystkich zainteresowanych, oto plany z tylko indeksem na Score
: