Ich weiß, dass COALESCE
es keine gute Praxis ist, ein paar Spalten zu bearbeiten und sich ihnen anzuschließen.
Das Generieren guter Kardinalitäts- und Verteilungsschätzungen ist schwierig genug, wenn das Schema 3NF + (mit Schlüsseln und Einschränkungen) und die Abfrage relational und in erster Linie SPJG (Auswahl-Projektion-Join-Gruppierung nach) ist. Das CE-Modell baut auf diesen Prinzipien auf. Je ungewöhnlicher oder nicht relationaler eine Abfrage ist, desto näher kommt man den Grenzen dessen, was das Kardinalitäts- und Selektivitäts-Framework handhaben kann. Gehen Sie zu weit und CE wird aufgeben und raten .
Der größte Teil des MCVE-Beispiels ist einfaches SPJ (kein G), allerdings mit überwiegend äußeren Equijoins (modelliert als inneres Join plus Antisemijoin) anstelle des einfacheren inneren Equijoins (oder Semijoins). Alle Relationen haben Schlüssel, jedoch keine Fremdschlüssel oder andere Einschränkungen. Alle bis auf einen Joins sind Eins-zu-Viele, was gut ist.
Die Ausnahme ist die Viele-zu-Viele- Verknüpfung zwischen X_DETAIL_1
und X_DETAIL_LINK
. Die einzige Funktion dieses Joins im MCVE ist das potenzielle Duplizieren von Zeilen in X_DETAIL_1
. Dies ist eine ungewöhnliche Sache.
Einfache Gleichheitsprädikate (Auswahlen) und Skalaroperatoren sind ebenfalls besser. Zum Beispiel funktioniert Attribut-Vergleich-gleiches Attribut / Konstante im Modell normalerweise gut. Es ist relativ "einfach", Histogramme und Häufigkeitsstatistiken zu ändern, um die Anwendung solcher Prädikate widerzuspiegeln.
COALESCE
wird auf gebaut CASE
, was wiederum intern als implementiert wird IIF
(und dies war schon lange wahr, bevor es IIF
in der Transact-SQL-Sprache erschien). Die CE - Modelle IIF
als ein UNION
mit zwei sich gegenseitig ausschließende Kinder, bestehend jeweils aus einem Projekt auf einer Auswahl an der Eingangs Beziehung. Jede der aufgelisteten Komponenten verfügt über eine Modellunterstützung, sodass das Kombinieren relativ einfach ist. Je mehr Abstraktionen überlagert werden, desto ungenauer ist das Endergebnis - ein Grund, warum größere Ausführungspläne in der Regel weniger stabil und zuverlässig sind.
ISNULL
Auf der anderen Seite ist intrinsische zum Motor. Es wird nicht mehr mit Grundkomponenten aufgebaut. Das Anwenden des Effekts ISNULL
auf ein Histogramm ist beispielsweise so einfach wie das Ersetzen des Schritts für NULL
Werte (und das Komprimieren nach Bedarf). Es ist immer noch relativ undurchsichtig, wie es skalare Operatoren tun, und wird daher am besten vermieden, wenn dies möglich ist. Trotzdem ist es im Allgemeinen optimiererfreundlicher (weniger optimiererunfreundlich) als ein auf CASE
-basierendes alternatives Verfahren.
Das CE (70 und 120+) ist selbst für SQL Server-Standards sehr komplex. Es geht nicht darum, jedem Operator eine einfache Logik (mit einer geheimen Formel) zuzuweisen. Das CE kennt Schlüssel und funktionale Abhängigkeiten. Es kann anhand von Häufigkeiten, multivariaten Statistiken und Histogrammen schätzen. und es gibt jede Menge Sonderfälle, Verfeinerungen, Checks & Balances und unterstützende Strukturen. Sie schätzt zB Verknüpfungen häufig auf mehrere Arten (Häufigkeit, Histogramm) und entscheidet über ein Ergebnis oder eine Anpassung basierend auf den Unterschieden zwischen den beiden.
Eine letzte grundlegende Sache, die behandelt werden muss: Die erste Kardinalitätsschätzung wird für jede Operation in der Abfragestruktur von unten nach oben ausgeführt. Selektivität und Kardinalität werden zuerst für Blattoperatoren abgeleitet (Basisrelationen). Geänderte Histogramme und Dichte- / Frequenzinformationen werden für übergeordnete Bediener abgeleitet. Je höher der Baum, desto geringer ist die Qualität der Schätzungen, da sich häufig Fehler ansammeln.
Diese einzelne anfängliche umfassende Schätzung stellt einen Ausgangspunkt dar und tritt auf, lange bevor ein endgültiger Ausführungsplan in Betracht gezogen wird (dies geschieht weit vor der Kompilierungsphase des Trivialplans). Der Abfragebaum an dieser Stelle spiegelt in der Regel die geschriebene Form der Abfrage ziemlich genau wider (obwohl Unterabfragen entfernt und Vereinfachungen angewendet wurden usw.).
Unmittelbar nach der ersten Schätzung führt SQL Server eine heuristische Join-Neuordnung durch, bei der lose gesagt versucht wird, die Struktur neu anzuordnen, um kleinere Tabellen und Joins mit hoher Selektivität zuerst zu platzieren. Es wird auch versucht, innere Verknüpfungen vor äußeren Verknüpfungen zu positionieren und Produkte zu kreuzen. Seine Fähigkeiten sind nicht umfangreich; ihre Bemühungen sind nicht erschöpfend; und physische Kosten werden nicht berücksichtigt (da sie noch nicht existieren - nur statistische Informationen und Metadateninformationen sind vorhanden). Die heuristische Neuordnung ist am erfolgreichsten bei einfachen inneren Equijoin-Bäumen. Sie soll einen "besseren" Ausgangspunkt für eine kostenbasierte Optimierung bieten.
Warum ist diese Schätzung der Join-Kardinalität so groß?
Die MCVE hat eine "ungewöhnliche", meist redundante Viele-zu-Viele- Verknüpfung und eine Equi-Verknüpfung mit COALESCE
dem Prädikat. Der Operator-Baum hat auch einen inneren Verknüpfungs- Letzten , der durch die heuristische Verknüpfungs-Neuordnung nicht an eine bevorzugtere Position verschoben werden konnte. Abgesehen von allen Skalaren und Projektionen lautet der Verknüpfungsbaum wie folgt:
LogOp_Join [ Card=4.52803e+009 ]
LogOp_LeftOuterJoin [ Card=481577 ]
LogOp_LeftOuterJoin [ Card=481577 ]
LogOp_LeftOuterJoin [ Card=481577 ]
LogOp_LeftOuterJoin [ Card=481577 ]
LogOp_Get TBL: X_DRIVING_TABLE(alias TBL: dt) [ Card=481577 ]
LogOp_Get TBL: X_DETAIL_1(alias TBL: d1) [ Card=70 ]
LogOp_Get TBL: X_DETAIL_LINK(alias TBL: lnk) [ Card=47 ]
LogOp_Get TBL: X_DETAIL_2(alias TBL: d2) X_DETAIL_2 [ Card=119 ]
LogOp_Get TBL: X_DETAIL_3(alias TBL: d3) X_DETAIL_3 [ Card=281 ]
LogOp_Get TBL: X_LAST_TABLE(alias TBL: lst) X_LAST_TABLE [ Card=94025 ]
Beachten Sie, dass die fehlerhafte endgültige Schätzung bereits vorliegt. Es wird als Card=4.52803e+009
Gleitkommawert mit doppelter Genauigkeit 4.5280277425e + 9 (4528027742.5 in Dezimal) gedruckt und intern gespeichert.
Die abgeleitete Tabelle in der ursprünglichen Abfrage wurde entfernt und die Projektionen normalisiert. Eine SQL-Darstellung des Baums, an dem die erste Kardinalitäts- und Selektivitätsschätzung durchgeführt wurde, ist:
SELECT
PRIMARY_ID = COALESCE(d1.JOIN_ID, d2.JOIN_ID, d3.JOIN_ID)
FROM X_DRIVING_TABLE dt
LEFT OUTER JOIN X_DETAIL_1 d1
ON dt.ID = d1.ID
LEFT OUTER JOIN X_DETAIL_LINK lnk
ON d1.LINK_ID = lnk.LINK_ID
LEFT OUTER JOIN X_DETAIL_2 d2
ON dt.ID = d2.ID
LEFT OUTER JOIN X_DETAIL_3 d3
ON dt.ID = d3.ID
INNER JOIN X_LAST_TABLE lst
ON lst.JOIN_ID = COALESCE(d1.JOIN_ID, d2.JOIN_ID, d3.JOIN_ID)
(Abgesehen davon ist die Wiederholung COALESCE
auch im endgültigen Plan vorhanden - einmal im endgültigen Berechnungsskalar und einmal auf der Innenseite des inneren Joins).
Beachten Sie die endgültige Verknüpfung. Diese innere Verknüpfung ist (per Definition) das kartesische Produkt von X_LAST_TABLE
und der vorhergehenden Verknüpfungsausgabe, wobei eine Auswahl (Verknüpfungsprädikat) von lst.JOIN_ID = COALESCE(d1.JOIN_ID, d2.JOIN_ID, d3.JOIN_ID)
angewendet wird. Die Kardinalität des kartesischen Produkts ist einfach 481577 * 94025 = 45280277425.
Dazu müssen wir die Selektivität des Prädikats bestimmen und anwenden. Die Kombination des undurchsichtigen erweiterten COALESCE
Baums (in Bezug auf UNION
und IIF
, denken Sie daran) zusammen mit der Auswirkung auf die Schlüsselinformationen, abgeleiteten Histogramme und Häufigkeiten der früheren "ungewöhnlichen", meist redundanten, kombinierten Viele-zu-Viele-Außenverknüpfung bedeutet, dass das CE nicht dazu in der Lage ist Leiten Sie eine akzeptable Schätzung auf eine der normalen Arten ab.
Infolgedessen wird die Guess Logic aufgerufen. Die Vermutungslogik ist mäßig komplex, wobei Schichten von "gebildeten" Vermutungsalgorithmen und "nicht so gebildeten" Vermutungsalgorithmen ausprobiert werden. Wenn keine bessere Basis für eine Vermutung gefunden wird, verwendet das Modell eine Vermutung des letzten Auswegs, die für einen Gleichheitsvergleich wie folgt lautet: sqllang!x_Selectivity_Equal
= feste 0,1-Selektivität (10% Vermutung):
-- the moment of doom
movsd xmm0,mmword ptr [sqllang!x_Selectivity_Equal
Das Ergebnis ist 0,1 Selektivität für das kartesische Produkt: 481577 * 94025 * 0,1 = 4528027742,5 (~ 4,52803e + 009), wie zuvor erwähnt.
Schreibt neu
Wenn die problematische Verknüpfung auskommentiert wird , wird eine bessere Schätzung erstellt, da die feste Selektivität "Vermutung des letzten Auswegs" vermieden wird (Schlüsselinformationen werden von den 1-M- Verknüpfungen beibehalten). Die Qualität der Schätzung ist immer noch wenig zuverlässig, da ein COALESCE
Join-Prädikat überhaupt nicht CE-kompatibel ist. Die revidierte Schätzung hat zumindest Blick vernünftigen Menschen, nehme ich an.
Wenn die Abfrage so geschrieben wird, dass der äußere Join X_DETAIL_LINK
den letzten Platz einnimmt, kann die heuristische Neuordnung sie durch den endgültigen inneren Join ersetzen X_LAST_TABLE
. Wenn Sie den inneren Join direkt neben das Problem stellen, können Sie mit den eingeschränkten Möglichkeiten einer frühen Nachbestellung die endgültige Schätzung verbessern, da die Auswirkungen des meist redundanten "ungewöhnlichen" äußeren Joins nach der heiklen Selektivitätsschätzung auftreten für COALESCE
. Auch hier sind die Schätzungen kaum besser als feste Vermutungen und würden einer entschlossenen gerichtlichen Gegenprüfung wahrscheinlich nicht standhalten.
Das Neuordnen einer Mischung aus inneren und äußeren Verknüpfungen ist schwierig und zeitaufwendig (selbst bei der vollständigen Optimierung in Stufe 2 wird nur eine begrenzte Teilmenge der theoretischen Schritte versucht).
Die ISNULL
in Max Vernons Antwort vorgeschlagene Verschachtelung vermeidet die festgelegte Rettung, aber die endgültige Schätzung ist eine unwahrscheinliche Null (aus Gründen des Anstands zu einer Zeile angehoben). Dies könnte genauso gut eine feste Schätzung von 1 Zeile sein, für alle statistischen Grundlagen, die die Berechnung hat.
Ich würde eine Join-Kardinalitätsschätzung zwischen 0 und 481577 Zeilen erwarten.
Dies ist eine vernünftige Erwartung, auch wenn man akzeptiert, dass die Kardinalitätsschätzung zu unterschiedlichen Zeitpunkten (während der kostenbasierten Optimierung) auf physikalisch unterschiedlichen, aber logisch und semantisch identischen Teilbäumen erfolgen kann - wobei der endgültige Plan eine Art zusammengenähter Best-Of-Plan ist am besten (pro Memogruppe). Das Fehlen einer planweiten Konsistenzgarantie bedeutet nicht, dass ein einzelner Join in der Lage sein sollte, sich über Seriosität hinwegzusetzen, das verstehe ich.
Auf der anderen Seite ist die Hoffnung schon verloren, wenn wir die Vermutung des letzten Auswegs haben. Warum also die Mühe machen? Wir haben alle Tricks ausprobiert und aufgegeben. Nicht zuletzt ist die wilde Endabschätzung ein gutes Warnsignal dafür, dass bei der Kompilierung und Optimierung dieser Abfrage im CE nicht alles gut gelaufen ist.
Als ich das MCVE ausprobierte, erstellte das 120+ CE eine Null (= 1) -Zeilenendschätzung (wie die verschachtelte ISNULL
) für die ursprüngliche Abfrage, was für meine Denkweise ebenso inakzeptabel ist.
Die eigentliche Lösung besteht wahrscheinlich in einer Designänderung, um einfache Equi-Joins ohne COALESCE
oder ISNULL
und im Idealfall Fremdschlüssel und andere für die Abfragekompilierung nützliche Einschränkungen zu ermöglichen.
bigint
anstatt zu erhalten, erhaltendecimal(18, 0)
Sie Vorteile: 1) Verwenden Sie 8 Bytes anstelle von 9 für jeden Wert, und 2) Verwenden Sie einen bytevergleichbaren Datentyp anstelle eines gepackten Datentyps, was Auswirkungen haben kann für die CPU-Zeit beim Vergleichen von Werten.