Das Abfrageoptimierungsprogramm verfügt über n-ary-Operatoren, obwohl die Ausführungsengine eher weniger hat. Zur Veranschaulichung werde ich eine vereinfachte Version Ihrer Tabellen verwenden - (SQL Fiddle) .
SELECT DISTINCT
number
INTO foo
FROM master..spt_values
WHERE
number < 1000;
SELECT DISTINCT
number
INTO boo
FROM master..spt_values
WHERE
number between 300 and 1005;
SELECT DISTINCT
number
INTO koo
FROM master..spt_values
WHERE
number > 1006;
ALTER TABLE dbo.foo ADD PRIMARY KEY (number);
ALTER TABLE dbo.boo ADD PRIMARY KEY (number);
ALTER TABLE dbo.koo ADD PRIMARY KEY (number);
Schauen wir uns angesichts dieser Tabellen und Daten den Eingabebaum für eine Drei-Wege- UNION
Abfrage an:
SELECT f.number FROM dbo.foo AS f
UNION
SELECT b.number FROM dbo.boo AS b
UNION
SELECT k.number FROM dbo.koo AS k
OPTION (QUERYTRACEON 3604, QUERYTRACEON 8605);
LogOp_Union
OUTPUT(COL: Union1006 )
CHILD(QCOL: [f].number)
CHILD(QCOL: [b].number)
CHILD(QCOL: [k].number)
LogOp_Project
LogOp_Get TBL: dbo.foo(alias TBL: f)
AncOp_PrjList
LogOp_Project
LogOp_Get TBL: dbo.boo(alias TBL: b)
AncOp_PrjList
LogOp_Project
LogOp_Get TBL: dbo.koo(alias TBL: k)
AncOp_PrjList
Der logische Vereinigungsoperator hat einen Ausgang und drei untergeordnete Eingänge. Nach der kostenbasierten Optimierung ist der ausgewählte physische Baum eine Zusammenführungsverbindung mit drei Eingaben:
SELECT f.number FROM dbo.foo AS f
UNION
SELECT b.number FROM dbo.boo AS b
UNION
SELECT k.number FROM dbo.koo AS k
OPTION (QUERYTRACEON 3604, QUERYTRACEON 8607);
PhyOp_MergeUnion
PhyOp_Range TBL: dbo.foo(alias TBL: f)(1) ASC
PhyOp_Range TBL: dbo.boo(alias TBL: b)(1) ASC
PhyOp_Range TBL: dbo.koo(alias TBL: k)(1) ASC
Die Ausgabe des Optimierers wird in eine Form überarbeitet, die die Ausführungs-Engine (ohne n-ary Merge Union) verarbeiten kann:
Durch das Umschreiben nach der Optimierung wird das N-Ary PhyOp_MergeUnion
in mehrere Merge Union-Operatoren aufgeteilt. Beachten Sie, dass alle geschätzten Kosten mit dem "ursprünglichen" Gewerkschaftsbetreiber verbunden bleiben - die anderen haben eine Kostenschätzung von Null.
Die Gründe des Optimierers für Gewerkschaften, die n-ary-Operatoren verwenden, liefern eine Erklärung dafür, warum es nicht in Betracht gezogen wird, Ihr erstes Beispiel in denselben Plan wie das zweite Beispiel umzuschreiben (die Drei-Wege-Vereinigung ist ein einzelner Baumknoten).
Der zweite Grund ist, dass es keine Einschränkungen gibt, um die „fehlende Überlappung“ durchzusetzen. Bevor Einschränkungen bestehen, kann eine Vereinigung zwischen boo
und es koo
kann nicht garantiert werden, dass keine Duplikate erzeugt werden. Daher erhalten wir einen Plan zum Entfernen von Duplikaten (in diesem Fall eine Zusammenführungsunion):
SELECT b.number FROM dbo.boo AS b
UNION
SELECT k.number FROM dbo.koo AS k;
Durch Hinzufügen der folgenden Einschränkungen wird sichergestellt, dass die Nichtüberlappungsbedingung nicht verletzt werden kann, ohne die zwischengespeicherten Pläne für die Abfrage ungültig zu machen:
ALTER TABLE dbo.foo WITH CHECK ADD CHECK (number < 1000);
ALTER TABLE dbo.boo WITH CHECK ADD CHECK (number BETWEEN 300 AND 1005);
ALTER TABLE dbo.koo WITH CHECK ADD CHECK (number > 1006);
Jetzt kann der Optimierer sicher einfach verketten:
Selbst mit diesen Einschränkungen wird die Drei-Wege-Vereinigungsabfrage immer noch als drei Vereinigungen angezeigt, da der Optimierer normalerweise nicht in Betracht zieht, n-ary-Operatoren aufzuteilen, um Alternativen zu untersuchen. Der n-ary-Operator ist sehr nützlich, um den Suchraum unter Kontrolle zu halten. Ein Auseinanderbrechen wäre oft kontraproduktiv, da der Optimierer das Ziel hat, schnell einen guten Plan zu finden.
SELECT f.number FROM dbo.foo AS f
UNION
SELECT b.number FROM dbo.boo AS b
UNION
SELECT k.number FROM dbo.koo AS k;
Wenn als UNION
und geschrieben UNION ALL
, kann ein n-ary-Operator nicht mehr verwendet werden (die Typen stimmen nicht überein), sodass der Baum separate Knoten hat:
SELECT f.number FROM dbo.foo AS f
UNION
SELECT b.number FROM dbo.boo AS b
UNION ALL
SELECT k.number FROM dbo.koo AS k
OPTION (QUERYTRACEON 3604, QUERYTRACEON 8605);
LogOp_UnionAll
OUTPUT(COL: Union1007 )
CHILD(COL: Union1004 )
CHILD(QCOL: [k].number)
LogOp_Union
OUTPUT(COL: Union1004 )
CHILD(QCOL: [f].number)
CHILD(QCOL: [b].number)
LogOp_Project
LogOp_Get TBL: dbo.foo(alias TBL: f)
AncOp_PrjList
LogOp_Project
LogOp_Get TBL: dbo.boo(alias TBL: b)
AncOp_PrjList
LogOp_Project
LogOp_Get TBL: dbo.koo(alias TBL: k)
AncOp_PrjList
MERGE
join ist optimierter alsSORT
. Es hat eine lineare Komplexität und erfordert keine Speicherzuweisung.