Warum verwendet diese Abfrage keine Index-Spool?

23

Ich stelle diese Frage, um das Verhalten des Optimierers besser zu verstehen und die Grenzen der Index-Spools zu verstehen. Angenommen, ich lege Ganzzahlen von 1 bis 10000 auf einen Haufen:

CREATE TABLE X_10000 (ID INT NOT NULL);
truncate table X_10000;

INSERT INTO X_10000 WITH (TABLOCK)
SELECT TOP 10000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

Und erzwinge einen Nested Loop Join mit MAXDOP 1:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID = b.ID
OPTION (LOOP JOIN, MAXDOP 1);

Dies ist eine ziemlich unfreundliche Aktion gegenüber SQL Server. Joins mit verschachtelten Schleifen sind oft keine gute Wahl, wenn beide Tabellen keine relevanten Indizes haben. Hier ist der Plan:

schlechte Abfrage

Die Abfrage auf meinem Computer dauert 13 Sekunden, wenn 100000000 Zeilen vom Tabellenspool abgerufen wurden. Ich verstehe jedoch nicht, warum die Abfrage langsam sein muss. Das Abfrageoptimierungsprogramm kann Indizes im laufenden Betrieb über Index-Spools erstellen . Diese Abfrage scheint ein perfekter Kandidat für eine Index-Spool zu sein.

Die folgende Abfrage gibt dieselben Ergebnisse wie die erste zurück, verfügt über eine Index-Spool und wird in weniger als einer Sekunde beendet:

SELECT *
FROM X_10000 a
CROSS APPLY (SELECT TOP (9223372036854775807) b.ID FROM X_10000 b WHERE a.ID = b.ID) ca
OPTION (LOOP JOIN, MAXDOP 1);

Problemumgehung 1

Diese Abfrage hat auch eine Index-Spool und endet in weniger als einer Sekunde:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (LOOP JOIN, MAXDOP 1);

Problemumgehung 2

Warum hat die ursprüngliche Abfrage keine Index-Spool? Gibt es eine Reihe von dokumentierten oder undokumentierten Hinweisen oder Ablaufverfolgungsflags, die eine Index-Spool ergeben? Ich habe diese verwandte Frage gefunden , aber sie beantwortet meine Frage nicht vollständig und ich kann das mysteriöse Ablaufverfolgungsflag nicht für diese Abfrage verwenden.

Joe Obbish
quelle

Antworten:

20

Wie Sie wissen, ist die Suche des Optimierers nicht vollständig. Es werden Dinge ausprobiert, die im Kontext sinnvoll sind und sich häufig für echte Abfragen auszahlen. Das Erzwingen eines Loop-Joins zwischen zwei einspaltigen nicht indizierten Heap-Tabellen ist kein solches Szenario. Das heißt, hier sind einige Details:

SQL Server transformiert gerne frühzeitig Joins, da er mehr Tricks mit Joins kennt. Später wird möglicherweise untersucht, wie Sie den Join wieder in eine Anwendung umwandeln können. Der Unterschied zwischen den beiden korrelierten Parametern (äußere Referenzen). Gilt sinnvoll, wenn auf der Innenseite ein passender Index vorhanden ist. In Ihrem Beispiel sind keine Indizes vorhanden, sodass das Optimierungsprogramm nicht dazu überredet ist, die Übersetzung in eine Anwendung zu untersuchen.

Bei einer einfachen (nicht zutreffenden) Verknüpfung wird das Verknüpfungsprädikat auf den Verknüpfungsoperator anstatt auf äußere Referenzen angewendet. Die Spool-Optimierung für eine Nichtanwendung ist in der Regel eine verzögerte Tabellenspule, da nur beim Join kein Vergleichselement auf der Innenseite vorhanden ist.

Das Optimierungsprogramm erwägt nicht, einen Index im laufenden Betrieb zu erstellen, um eine Anwendung zu ermöglichen. Vielmehr ist die Reihenfolge der Ereignisse normalerweise umgekehrt: Transformiere, um anzuwenden, weil ein guter Index existiert.

Sie können manchmal eine Anwendung anstelle einer Verknüpfung fördern, indem Sie APPLYin Ihrer Abfrage die Syntax verwenden. Das undokumentierte Ablaufverfolgungsflag 9114 kann dies unterstützen, indem es den Optimierer davon abhält, ein logisches Apply für einen Join im Voraus zu übersetzen. Beispielsweise:

SELECT * 
FROM dbo.X_1000 AS a
CROSS APPLY (SELECT * FROM dbo.X_1000 AS b WHERE b.ID = a.ID) AS b
OPTION (QUERYTRACEON 9114);

Spool-Plan

Ein Index-Spool wird zum Anwenden bevorzugt, da der äußere Verweis bedeutet, dass die Auswahl auf der Innenseite des Joins angewendet wird. Sie werden dies oft über sehen, SelToIndexOnTheFlyaber es gibt andere Pfade. Siehe meinen Artikel The Eager Index Spool und The Optimizer .

Paul White sagt GoFundMonica
quelle