Warum verwenden diese ähnlichen Abfragen unterschiedliche Optimierungsphasen (Transaktionsverarbeitung vs. Schnellplan)?


12

Der Beispielcode in diesem Verbindungselement

Zeigt einen Fehler wo

SELECT COUNT(*)
FROM   dbo.my_splitter_1('2') L1
       INNER JOIN dbo.my_splitter_1('') L2
         ON L1.csv_item = L2.csv_item

Liefert die korrekten Ergebnisse. Das Folgende gibt jedoch falsche Ergebnisse zurück (2014 mit dem neuen Kardinalitätsschätzer)

SELECT
    (SELECT COUNT(*)
    FROM dbo.my_splitter_1('2') L1
     INNER JOIN dbo.my_splitter_1('') L2
        ON L1.csv_item = L2.csv_item)

Wenn die Ergebnisse für L2 fälschlicherweise in eine gemeinsame Unterausdrucks-Spool geladen werden, wird das Ergebnis für das L1-Ergebnis erneut abgespielt.

Ich war neugierig, warum sich die beiden Fragen unterschiedlich verhielten. Trace Flag 8675 zeigt, dass derjenige, der funktioniert, search(0) - transaction processingund derjenige, der fehlschlägt, eintrifft search(1) - quick plan.

Daher gehe ich davon aus, dass die Verfügbarkeit zusätzlicher Transformationsregeln für das unterschiedliche Verhalten verantwortlich ist (das Deaktivieren von BuildGbApply oder GenGbApplySimple scheint dies beispielsweise zu beheben).

Warum stoßen die beiden Pläne für diese sehr ähnlichen Abfragen auf unterschiedliche Optimierungsphasen? Nach dem, was ich gelesen habe, search (0)sind mindestens drei Tabellen erforderlich, und diese Bedingung ist im ersten Beispiel sicherlich nicht erfüllt.

Antworten:


7

Jede Etappe hat Einreisebedingungen. "Mindestens drei Tabellenverweise haben" ist eine der Einstiegsbedingungen, über die wir sprechen, wenn wir einfache Beispiele geben, aber es ist nicht die einzige.

Im Allgemeinen sind nur Basis-Joins und -Unions für die Suche nach 0 zulässig. Skalare Unterabfragen, Semi-Joins usw. verhindern den Zugriff auf die Suche 0. Diese Phase ist wirklich für die sehr gebräuchlichen OLTP-Abfrageformen vorgesehen. Die Regeln, die notwendig sind, um die weniger verbreiteten Dinge zu erforschen, sind einfach nicht aktiviert. Ihre Beispielabfrage hat eine skalare Unterabfrage, daher schlägt die Eingabe fehl.

Es hängt auch davon ab, wie Sie Tabellenreferenzen zählen. Ich habe mich mit Funktionen noch nie eingehend befasst, aber es ist möglich, dass die Logik sowohl die Tabellenwertfunktionen als auch die von ihnen erzeugten Tabellenvariablen zählt. Es könnte sogar sein, dass der Tabellenverweis in der Funktion selbst gezählt wird - ich bin mir nicht sicher; obwohl ich weiß, dass Funktionen rundum nur harte Arbeit sind.

Der Bug mit GenGbApplySimpleist hässlich. Diese Planform war immer eine Möglichkeit, wurde jedoch aus Kostengründen abgelehnt, bis die Änderung der angenommenen variablen Kardinalität der Tabelle auf 100 Zeilen eintrat. Es ist beispielsweise möglich, die problematische Planform mit einem USE PLANHinweis auf das CE vor 2014 zu erzwingen .

Sie haben Recht mit dem neuen Connect-Element um dasselbe Problem handelt , das zuvor gemeldet wurde .

Mit der folgenden Abfrage kann beispielsweise nach 0 gesucht werden:

DECLARE @T AS table (c1 integer NULL);

SELECT U.c1, rn = ROW_NUMBER() OVER (ORDER BY U.c1) 
FROM 
(
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
) AS U;

Wenn Sie eine kleine Änderung vornehmen, um eine skalare Unterabfrage einzuschließen, wird direkt nach 1 gesucht:

DECLARE @T AS table (c1 integer NULL);

SELECT U.c1, rn = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -- Changed!
FROM 
(
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
) AS U;
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.