Wann können SARGable-Prädikate in einen CTE oder eine abgeleitete Tabelle verschoben werden?

15

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;

NÜSSE

Das SARGable-Prädikat für Score wird nicht in den CTE verschoben. Es ist in einem Filter Operator viel später im Plan.

NÜSSE

Was ich merkwürdig finde, da das ORDER BYauf 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.

NÜSSE

NÜSSE

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

Link planen .

NÜSSE

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:

Erik Darling
quelle

Antworten:

11

Hier sind einige Probleme im Spiel.

Prädikate vorbeischieben TOP

Das Optimierungsprogramm kann derzeit kein Prädikat hinter a verschieben TOP, selbst in den wenigen Fällen, in denen dies ohne weiteres möglich wäre *. Diese Einschränkung berücksichtigt das Verhalten aller Abfragen in der Frage, bei denen sich das Prädikat in einem höheren Bereich als das befindet TOP.

Die Umgehung besteht darin, das Neuschreiben manuell durchzuführen. Das grundlegende Problem ähnelt dem Fall , in dem Prädikate an einer Fensterfunktion vorbeigeführt werden , mit der Ausnahme, dass es keine entsprechende spezialisierte Regel gibt SelOnSeqPrj.

Meine persönliche Meinung ist, dass eine Explorationsregel wie diese nicht SelOnTopumgesetzt wird, weil die Leute absichtlich Anfragen geschrieben haben TOP, um eine Art "Optimierungszaun" zu schaffen.

* Im Allgemeinen bedeutet dies, dass das Prädikat in der ORDER BYmit dem assoziierten Klausel erscheinen sollteTOP und die Richtung einer etwaigen Ungleichung mit der Sortierrichtung übereinstimmen sollte. Die Umwandlung muss auch das Sortierverhalten von NULL-Werten in SQL Server berücksichtigen. Insgesamt dürfte diese Transformation aufgrund der Einschränkungen in der Praxis nicht sinnvoll genug sein, um die zusätzlichen Explorationsbemühungen zu rechtfertigen.

Kostenprobleme

Die verbleibenden Ausführungspläne in der Frage können als kostenbasierte Auswahl aufgrund der Werteverteilung in der ScoreSpalte (viel mehr Zeilen <= 500 als> = 500) und der Auswirkung des Zeilenziels erklärt werden, das durch das eingeführt wird TOP.

Zum Beispiel die Abfrage:

--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;

... erstellt einen Plan mit einem scheinbar nicht komprimierten Prädikat in einem Filter:

später Filter wegen Zeilenziel

Beachten Sie, dass die Sortierung schätzungsweise 101 Zeilen ergibt. Dies ist der Effekt des Zeilenziels, das von Top hinzugefügt wird. Dies wirkt sich auf die geschätzten Kosten der Sortierung und des Filters aus, sodass es so aussieht, als wäre dies die billigere Option. Die geschätzten Kosten für diesen Plan betragen 2401,39 Einheiten.

Wenn wir Zeilenziele mit einem Abfragehinweis deaktivieren:

--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
OPTION (USE HINT ('DISABLE_OPTIMIZER_ROWGOAL'));

... der erstellte Ausführungsplan lautet:

Planen ohne Reihenziel

Das Prädikat wurde als verbleibendes nicht sargierbares Prädikat in den Scan übernommen, und die Kosten für den gesamten Plan betragen 2402,32 Einheiten.

Beachten Sie, dass das <= 500Prädikat keine Zeilen herausfiltern soll. Wenn Sie beispielsweise eine kleinere Zahl gewählt <= 50hätten, hätte das Optimierungsprogramm den Push-Prädikat-Plan unabhängig vom Zeilenzieleffekt bevorzugt.

Für die Abfrage mit Score DESCund einem Score >= 500Prädikat:

--Written normally
SELECT TOP (101)
    c.UserId, 
    c.[Text],
    c.Score
FROM dbo.Comments AS c
WHERE
    c.Score >= 500
ORDER BY
    c.Score DESC;

Nun wird erwartet, dass das Prädikat sehr selektiv ist, sodass der Optimierer das Prädikat pushen und den nicht gruppierten Index mit Suchvorgängen verwenden möchte:

selektives Prädikat

Das Optimierungsprogramm hat wiederum mehrere Alternativen in Betracht gezogen und dies wie üblich als die anscheinend billigste Option ausgewählt.

Paul White Monica wieder einsetzen
quelle