Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Mehrere INSERT-Anweisungen vs. einzelne INSERT-Anweisungen mit mehreren WERTEN

Zusatz: SQL Server 2012 zeigt eine etwas verbesserte Leistung in diesem Bereich, scheint aber die unten aufgeführten spezifischen Probleme nicht zu lösen. Dies sollte anscheinend in der nächsten Hauptversion nach behoben werden SQL Server 2012!

Ihr Plan zeigt, dass die einzelnen Einfügungen parametrisierte Prozeduren verwenden (möglicherweise automatisch parametrisiert), sodass die Parsing-/Kompilierungszeit für diese minimal sein sollte.

Ich dachte, ich würde mir das ein bisschen genauer ansehen, also habe ich eine Schleife (Skript) eingerichtet und versucht, die Anzahl der VALUES anzupassen Klauseln und Aufzeichnung der Kompilierzeit.

Ich habe dann die Kompilierzeit durch die Anzahl der Zeilen geteilt, um die durchschnittliche Kompilierzeit pro Klausel zu erhalten. Die Ergebnisse finden Sie unten

Bis zu 250 VALUES Klauseln präsentieren die Kompilierzeit / Anzahl der Klauseln hat einen leichten Aufwärtstrend, aber nichts allzu Dramatisches.

Aber dann gibt es eine plötzliche Veränderung.

Dieser Abschnitt der Daten ist 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, fällt plötzlich ab, aber CompileTime erhöht sich um das 7-fache und CompileMemory schießt in die Höhe. Dies ist der Schnittpunkt 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).

Nicht sicher, warum das sein sollte. Vermutlich muss es beim Kompilieren eines Plans für bestimmte Literalwerte einige Aktivitäten ausführen, die nicht linear skaliert werden (z. B. Sortieren).

Es scheint sich nicht auf die Größe des zwischengespeicherten Abfrageplans auszuwirken, als ich eine Abfrage ausprobierte, die vollständig aus doppelten Zeilen bestand, und es wirkt sich auch nicht auf die Reihenfolge der Ausgabe der Tabelle der Konstanten aus (und da Sie Zeit in einen Heap einfügen, die für das Sortieren aufgewendet wird wäre sowieso sinnlos, selbst wenn es so wäre).

Darüber hinaus zeigt der Plan, wenn der Tabelle ein gruppierter Index hinzugefügt wird, immer noch einen expliziten Sortierschritt, sodass es nicht so scheint, als würde er zur Kompilierzeit sortieren, um eine Sortierung zur Laufzeit zu vermeiden.

Ich habe versucht, mir das in einem Debugger anzusehen, aber die öffentlichen Symbole für meine Version von SQL Server 2008 scheinen nicht verfügbar zu sein, also musste ich mir stattdessen das Äquivalent UNION ALL ansehen Konstruktion in SQL Server 2005.

Ein typischer Stack-Trace ist 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 man also die Namen im Stack-Trace verlässt, scheint es viel Zeit mit dem Vergleichen von Strings zu verbringen.

Dieser KB-Artikel weist darauf hin, dass DeriveNormalizedGroupProperties ist mit der sogenannten Normalisierungsphase der Abfrageverarbeitung

verbunden

Diese Phase wird jetzt als Bindung oder Algebrierung bezeichnet und nimmt die Ausgabe des Ausdrucks-Parse-Baums aus der vorherigen Parsing-Phase und gibt einen algebrisierten Ausdrucksbaum (Abfrageprozessorbaum) aus, um mit der Optimierung fortzufahren (Trivialplanoptimierung in diesem Fall) [ref].

Ich habe ein weiteres Experiment (Skript) versucht, das darin bestand, den ursprünglichen Test erneut auszuführen, aber drei verschiedene Fälle zu betrachten.

  1. Vorname und Nachname Zeichenfolgen mit einer Länge von 10 Zeichen ohne Duplikate.
  2. Vorname und Nachname Zeichenfolgen mit einer Länge von 50 Zeichen ohne Duplikate.
  3. Vorname und Nachname Zeichenfolgen mit einer Länge von 10 Zeichen mit allen Duplikaten.

Es ist deutlich zu sehen, dass es umso schlechter wird, je länger die Saiten sind, und umgekehrt, je mehr Duplikate es gibt, desto besser wird es. 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 vorhanden sein muss.

Bearbeiten

Ein Ort, an dem diese Informationen genutzt werden, ist @Lieven hier

SELECT * 
FROM (VALUES ('Lieven1', 1),
             ('Lieven2', 2),
             ('Lieven3', 3))Test (name, ID)
ORDER BY name, 1/ (ID - ID) 

Denn zur Kompilierzeit kann festgestellt werden, dass der Name Spalte keine Duplikate hat, wird die Sortierung nach dem sekundären 1/ (ID - ID) übersprungen Ausdruck zur Laufzeit (die Sortierung im Plan hat nur einen ORDER BY Spalte) und es wird kein Division-durch-Null-Fehler ausgelöst. Wenn der Tabelle Duplikate hinzugefügt werden, zeigt der Sortieroperator zwei Sortieren-nach-Spalten an und der erwartete Fehler wird ausgelöst.