Ergänzung: SQL Server 2012 weist in diesem Bereich eine verbesserte Leistung auf, scheint jedoch die unten aufgeführten spezifischen Probleme nicht zu lösen. Dies sollte anscheinend in der nächsten Hauptversion nach
SQL Server 2012 behoben werden !
Ihr Plan zeigt, dass die einzelnen Einfügungen parametrisierte Prozeduren verwenden (möglicherweise automatisch parametrisiert), sodass die Analyse- / Kompilierungszeit für diese minimal sein sollte.
Ich dachte, ich würde das etwas genauer untersuchen, also richte eine Schleife (ein Skript ) ein und versuchte, die Anzahl der VALUES
Klauseln anzupassen und die Kompilierungszeit aufzuzeichnen.
Ich habe dann die Kompilierungszeit durch die Anzahl der Zeilen geteilt, um die durchschnittliche Kompilierungszeit pro Klausel zu erhalten. Die Ergebnisse sind unten
Bis zu 250 VALUES
Klauseln weisen die Kompilierungszeit / Anzahl der Klauseln einen leichten Aufwärtstrend auf, aber nichts zu dramatisches.
Aber dann gibt es eine plötzliche Veränderung.
Dieser Abschnitt der Daten wird unten gezeigt.
+------+----------------+-------------+---------------+---------------+
| Rows | CachedPlanSize | CompileTime | CompileMemory | Duration/Rows |
+------+----------------+-------------+---------------+---------------+
| 245 | 528 | 41 | 2400 | 0.167346939 |
| 246 | 528 | 40 | 2416 | 0.162601626 |
| 247 | 528 | 38 | 2416 | 0.153846154 |
| 248 | 528 | 39 | 2432 | 0.157258065 |
| 249 | 528 | 39 | 2432 | 0.156626506 |
| 250 | 528 | 40 | 2448 | 0.16 |
| 251 | 400 | 273 | 3488 | 1.087649402 |
| 252 | 400 | 274 | 3496 | 1.087301587 |
| 253 | 400 | 282 | 3520 | 1.114624506 |
| 254 | 408 | 279 | 3544 | 1.098425197 |
| 255 | 408 | 290 | 3552 | 1.137254902 |
+------+----------------+-------------+---------------+---------------+
Die Größe des zwischengespeicherten Plans, die linear gewachsen war, sinkt plötzlich, aber CompileTime erhöht sich um das 7-fache und CompileMemory schießt hoch. Dies ist der Grenzwert zwischen einem automatisch parametrisierten Plan (mit 1.000 Parametern) und einem nicht parametrisierten Plan. Danach scheint es linear weniger effizient zu werden (in Bezug auf die Anzahl der in einer bestimmten Zeit verarbeiteten Wertklauseln).
Ich bin mir nicht sicher, warum das so sein sollte. Vermutlich muss beim Erstellen eines Plans für bestimmte Literalwerte eine Aktivität ausgeführt werden, die nicht linear skaliert wird (z. B. Sortieren).
Es scheint keinen Einfluss auf die Größe des zwischengespeicherten Abfrageplans zu haben, als ich eine Abfrage ausprobiert habe, die vollständig aus doppelten Zeilen besteht, und es wirkt sich auch nicht auf die Reihenfolge der Ausgabe der Tabelle der Konstanten aus (und beim Einfügen in einen Heap-Zeitaufwand für das Sortieren wäre sowieso sinnlos, selbst wenn es so wäre).
Wenn der Tabelle ein Clustered-Index hinzugefügt wird, zeigt der Plan weiterhin einen expliziten Sortierschritt an, sodass er zur Kompilierungszeit nicht sortiert zu werden scheint, um eine Sortierung zur Laufzeit zu vermeiden.
Ich habe versucht, dies in einem Debugger zu überprüfen, aber die öffentlichen Symbole für meine Version von SQL Server 2008 scheinen nicht verfügbar zu sein. Stattdessen musste ich mir die entsprechende UNION ALL
Konstruktion in SQL Server 2005 ansehen .
Eine typische Stapelverfolgung finden Sie unten
sqlservr.exe!FastDBCSToUnicode() + 0xac bytes
sqlservr.exe!nls_sqlhilo() + 0x35 bytes
sqlservr.exe!CXVariant::CmpCompareStr() + 0x2b bytes
sqlservr.exe!CXVariantPerformCompare<167,167>::Compare() + 0x18 bytes
sqlservr.exe!CXVariant::CmpCompare() + 0x11f67d bytes
sqlservr.exe!CConstraintItvl::PcnstrItvlUnion() + 0xe2 bytes
sqlservr.exe!CConstraintProp::PcnstrUnion() + 0x35e bytes
sqlservr.exe!CLogOp_BaseSetOp::PcnstrDerive() + 0x11a bytes
sqlservr.exe!CLogOpArg::PcnstrDeriveHandler() + 0x18f bytes
sqlservr.exe!CLogOpArg::DeriveGroupProperties() + 0xa9 bytes
sqlservr.exe!COpArg::DeriveNormalizedGroupProperties() + 0x40 bytes
sqlservr.exe!COptExpr::DeriveGroupProperties() + 0x18a bytes
sqlservr.exe!COptExpr::DeriveGroupProperties() + 0x146 bytes
sqlservr.exe!COptExpr::DeriveGroupProperties() + 0x146 bytes
sqlservr.exe!COptExpr::DeriveGroupProperties() + 0x146 bytes
sqlservr.exe!CQuery::PqoBuild() + 0x3cb bytes
sqlservr.exe!CStmtQuery::InitQuery() + 0x167 bytes
sqlservr.exe!CStmtDML::InitNormal() + 0xf0 bytes
sqlservr.exe!CStmtDML::Init() + 0x1b bytes
sqlservr.exe!CCompPlan::FCompileStep() + 0x176 bytes
sqlservr.exe!CSQLSource::FCompile() + 0x741 bytes
sqlservr.exe!CSQLSource::FCompWrapper() + 0x922be bytes
sqlservr.exe!CSQLSource::Transform() + 0x120431 bytes
sqlservr.exe!CSQLSource::Compile() + 0x2ff bytes
Wenn Sie also die Namen in der Stapelverfolgung entfernen, scheint es viel Zeit zu kosten, Zeichenfolgen zu vergleichen.
Dieser KB-Artikel gibt an, dass dies DeriveNormalizedGroupProperties
mit der sogenannten Normalisierungsphase der Abfrageverarbeitung verbunden ist
Diese Phase wird jetzt als Binden oder Algebrisieren bezeichnet. Sie verwendet die Ausgabe des Ausdrucksanalysebaums aus der vorherigen Analysestufe und gibt einen algebrisierten Ausdrucksbaum (Abfrageprozessorbaum) aus, um mit der Optimierung fortzufahren (in diesem Fall Trivialplanoptimierung) [ref] .
Ich habe ein weiteres Experiment ( Skript ) versucht , bei dem der ursprüngliche Test erneut ausgeführt wurde, wobei jedoch drei verschiedene Fälle untersucht wurden.
- Vorname und Nachname Zeichenfolgen mit einer Länge von 10 Zeichen ohne Duplikate.
- Vorname und Nachname Zeichenfolgen mit einer Länge von 50 Zeichen ohne Duplikate.
- Vorname und Nachname Zeichenfolgen mit einer Länge von 10 Zeichen mit allen Duplikaten.
Es ist deutlich zu sehen, dass je länger die Saiten sind, desto schlechter werden die Dinge und umgekehrt, je mehr Duplikate, desto besser werden die Dinge. Wie bereits erwähnt, wirken sich Duplikate nicht auf die Größe des zwischengespeicherten Plans aus. Daher gehe ich davon aus, dass beim Erstellen des algebrisierten Ausdrucksbaums selbst ein Prozess zur Identifizierung von Duplikaten erforderlich ist.
Bearbeiten
Ein Ort, an dem diese Informationen genutzt werden , wird hier von @Lieven angezeigt
SELECT *
FROM (VALUES ('Lieven1', 1),
('Lieven2', 2),
('Lieven3', 3))Test (name, ID)
ORDER BY name, 1/ (ID - ID)
Da zur Kompilierungszeit festgestellt werden kann, dass die Name
Spalte keine Duplikate enthält, wird die Reihenfolge nach dem sekundären 1/ (ID - ID)
Ausdruck zur Laufzeit übersprungen (die Sortierung im Plan enthält nur eine ORDER BY
Spalte) und es wird kein Fehler zum Teilen durch Null ausgelöst. Wenn der Tabelle Duplikate hinzugefügt werden, zeigt der Sortieroperator zwei Spalten an und der erwartete Fehler wird ausgelöst.