Sandsack
Bei der Arbeit an Top Quality Blog Posts® bin ich auf ein Optimierungsverhalten gestoßen, das mich sehr verärgert hat . Ich habe nicht sofort eine Erklärung, zumindest nicht eine, mit der ich zufrieden bin, also schreibe ich sie hier für den Fall, dass jemand klug auftaucht.
Wenn Sie mitmachen möchten, können Sie hier die Version 2013 des Stack Overflow-Datendumps herunterladen . Ich verwende die Kommentartabelle mit einem zusätzlichen Index.
CREATE INDEX [ix_ennui] ON [dbo].[Comments] ( [UserId], [Score] DESC );
Frage eins
Wenn ich die Tabelle wie folgt abfrage , erhalte ich einen ungeraden Abfrageplan .
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;
Das SARGable-Prädikat für Score wird nicht in den CTE verschoben. Es ist in einem Filter Operator viel später im Plan.
Was ich merkwürdig finde, da das ORDER BY
auf der gleichen Spalte steht wie der Filter.
Abfrage zwei
Wenn ich die Abfrage ändere, wird sie weitergeleitet.
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;
Der Abfrageplan ändert sich ebenfalls und wird viel schneller ausgeführt, ohne dass Daten auf die Festplatte gelangen. Beide führen zu denselben Ergebnissen, wobei das Prädikat beim Nonclustered-Index-Scan verwendet wird.
Abfrage drei
Dies entspricht dem Schreiben der Abfrage wie folgt:
SELECT TOP 101
c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
WHERE c.Score >= 500
ORDER BY c.Score DESC;
Abfrage vier
Bei Verwendung einer abgeleiteten Tabelle wird derselbe "fehlerhafte" Abfrageplan wie bei der ersten CTE-Abfrage abgerufen
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;
Noch seltsamer wird es, wenn ...
Ich ändere die Abfrage, um die Daten aufsteigend zu ordnen, und den Filter auf <=
.
Damit diese Frage nicht zu lang wird, werde ich alles zusammensetzen.
Abfragen
--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;
Pläne
Beachten Sie, dass keine dieser Abfragen den Nonclustered-Index nutzt. Hier ändert sich nur die Position des Filteroperators. In keinem Fall wird das Prädikat an den Indexzugriff weitergeleitet.
Eine Frage erscheint!
Gibt es einen Grund dafür, dass ein SARGable-Prädikat in einigen Szenarien und nicht in anderen Szenarien übertragen werden kann? Die Unterschiede innerhalb der Abfragen, die in absteigender Reihenfolge sortiert sind, sind interessant, aber die Unterschiede zwischen denen und denen, die aufsteigend sind, sind bizarr.
Für alle Interessierten sind hier die Pläne mit nur einem Index auf Score
: