it-swarm.com.de

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

Sandsack

Während ich an Top Quality Blog Posts® arbeitete, stieß ich auf ein Optimierungsverhalten, das ich wirklich fand ärgerlich interessant. Ich habe nicht sofort eine Erklärung, zumindest keine, mit der ich zufrieden bin, also setze ich sie hier ein, falls jemand klug auftaucht.

Wenn Sie mitmachen möchten, können Sie die Version 2013 des Stack Overflow Data Dump hier herunterladen. Ich verwende die Kommentartabelle mit einem zusätzlichen Index.

CREATE INDEX [ix_ennui] ON [dbo].[Comments] ( [UserId], [Score] DESC );

Abfrage Eins

Wenn ich die Tabelle so abfrage, erhalte ich einen ngeraden 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;

(NUTS

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

(NUTS

Was ich merkwürdig finde, da die ORDER BY befindet sich in derselben Spalte wie der Filter.

Abfrage zwei

Wenn ich die Abfrage ändere, wird sie gepusht.

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 läuft viel schneller, ohne dass etwas auf die Festplatte gelangt. Beide liefern die gleichen Ergebnisse mit dem Prädikat beim nicht gruppierten Index-Scan.

(NUTS

(NUTS

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

Die Verwendung einer abgeleiteten Tabelle erhält den gleichen "schlechten" Abfrageplan wie die anfängliche CTE-Abfrage

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 <=.

Um diese Frage nicht zu lang zu machen, werde ich alles zusammenstellen.

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

Plan Link .

(NUTS

Beachten Sie, dass keine dieser Abfragen den nicht gruppierten Index nutzt - das einzige, was sich hier ändert, ist die Position des Filteroperators. In keinem Fall wird das Prädikat auf den Indexzugriff verschoben.

Eine Frage erscheint!

Gibt es einen Grund, warum ein SARGable-Prädikat in einigen Szenarien und nicht in anderen Szenarien verschoben werden kann? Die Unterschiede innerhalb der Abfragen, die in absteigender Reihenfolge sortiert sind, sind interessant, aber die Unterschiede zwischen denen und denjenigen, die aufsteigend bizarr sind.

Für alle Interessierten sind hier die Pläne mit nur einem Index für Score:

15
Erik Darling

Hier sind einige Probleme im Spiel.

Prädikate an TOP vorbeischieben

Das Optimierungsprogramm kann derzeit kein Prädikat über ein TOP hinausschieben, selbst in den begrenzten Fällen, in denen dies sicher 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 befindet als TOP.

Die Problemumgehung besteht darin, das Umschreiben manuell durchzuführen. Das grundlegende Problem ähnelt dem Fall von Prädikate an einer Fensterfunktion vorbeischieben , außer dass es keine entsprechende spezielle Regel wie SelOnSeqPrj gibt.

Meine persönliche Meinung ist, dass eine Explorationsregel wie SelOnTop nicht implementiert wird, weil Leute absichtlich Abfragen mit TOP geschrieben haben, um eine Art 'Optimierungszaun' bereitzustellen.

* Im Allgemeinen bedeutet dies, dass das Prädikat in der ORDER BY -Klausel erscheinen sollte, die mit TOP verknüpft ist, und dass die Richtung einer Ungleichung mit der Richtung der Sortierung übereinstimmen sollte. Die Umwandlung müsste auch das Sortierverhalten von NULL-Werten in SQL Server berücksichtigen. Insgesamt bedeuten die Einschränkungen wahrscheinlich, dass diese Transformation in der Praxis im Allgemeinen nicht nützlich genug wäre, um die zusätzlichen Explorationsbemühungen zu rechtfertigen.

Kostenprobleme

Die verbleibenden Ausführungspläne in der Frage können aufgrund der Verteilung der Werte in der Spalte Score (viel mehr Zeilen <= 500 als> = 500) und der Auswirkung von - als kostenbasierte Auswahl erklärt werden Zeilenziel eingeführt durch 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 ungezwungenen Prädikat in einem Filter:

(late filter due to row goal

Beachten Sie, dass die Sortierung auf 101 Zeilen geschätzt wird. Dies ist der Effekt des von Top hinzugefügten Zeilenziels. Dies wirkt sich so stark auf die geschätzten Kosten der Sortierung und des Filters aus, dass der Eindruck entsteht, dass dies die billigere Option ist. 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:

(plan without row goal

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

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

Für die Abfrage mit Score DESC und einem Score >= 500 Prä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;

Jetzt wird erwartet, dass das Prädikat sehr selektiv ist, daher wählt der Optimierer das Prädikat Push und Verwenden Sie den nicht gruppierten Index mit Lookups:

(selective predicate

Auch hier erwog der Optimierer mehrere Alternativen und wählte diese wie üblich als die anscheinend billigste Option.

11
Paul White 9