Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Più istruzioni INSERT rispetto a una singola INSERT con più VALUES

Aggiunta: SQL Server 2012 mostra alcune prestazioni migliorate in quest'area, ma non sembra affrontare i problemi specifici indicati di seguito. Apparentemente questo dovrebbe essere risolto nella prossima versione principale dopo SQL Server 2012!

Il tuo piano mostra che i singoli inserti utilizzano procedure parametrizzate (possibilmente parametrizzate automaticamente), quindi il tempo di analisi/compilazione per questi dovrebbe essere minimo.

Ho pensato di esaminarlo un po' di più, quindi ho impostato un ciclo (script) e ho provato a regolare il numero di VALUES clausole e registrazione del tempo di compilazione.

Ho quindi diviso il tempo di compilazione per il numero di righe per ottenere il tempo medio di compilazione per clausola. I risultati sono sotto

Fino a 250 VALUES clausole presenti il ​​tempo di compilazione/numero di clausole ha una leggera tendenza al rialzo ma niente di troppo drammatico.

Ma poi c'è un cambiamento improvviso.

Quella sezione dei dati è mostrata di seguito.

+------+----------------+-------------+---------------+---------------+
| 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   |
+------+----------------+-------------+---------------+---------------+

La dimensione del piano memorizzato nella cache che era cresciuta in modo lineare diminuisce improvvisamente ma CompileTime aumenta di 7 volte e CompileMemory aumenta. Questo è il punto di interruzione tra il piano che è parametrizzato automaticamente (con 1.000 parametri) e uno non parametrizzato. Successivamente sembra diventare linearmente meno efficiente (in termini di numero di clausole di valore elaborate in un dato tempo).

Non sono sicuro del motivo per cui dovrebbe essere. Presumibilmente quando sta compilando un piano per valori letterali specifici, deve eseguire alcune attività che non si ridimensionano in modo lineare (come l'ordinamento).

Non sembra influenzare la dimensione del piano di query memorizzato nella cache quando ho provato una query composta interamente da righe duplicate e nessuno dei due influisce sull'ordine dell'output della tabella delle costanti (e mentre stai inserendo in un heap il tempo speso per l'ordinamento sarebbe comunque inutile anche se così fosse).

Inoltre, se viene aggiunto un indice cluster alla tabella, il piano mostra ancora un passaggio di ordinamento esplicito, quindi non sembra essere in fase di ordinamento in fase di compilazione per evitare un ordinamento in fase di esecuzione.

Ho provato a esaminarlo in un debugger ma i simboli pubblici per la mia versione di SQL Server 2008 non sembrano essere disponibili, quindi ho dovuto guardare l'equivalente UNION ALL costruzione in SQL Server 2005.

Una tipica traccia dello stack è al di sotto

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   

Quindi, eliminando i nomi nella traccia dello stack, sembra passare molto tempo a confrontare le stringhe.

Questo articolo della Knowledge Base indica che DeriveNormalizedGroupProperties è associato a quella che veniva chiamata la fase di normalizzazione dell'elaborazione delle query

Questa fase è ora chiamata associazione o algebrizzazione e prende l'output dell'albero di analisi dell'espressione dalla fase di analisi precedente e genera un albero delle espressioni algebrizzato (albero del processore di query) per procedere all'ottimizzazione (ottimizzazione del piano banale in questo caso) [ref].

Ho provato un altro esperimento (Script) che consisteva nel rieseguire il test originale ma esaminando tre casi diversi.

  1. Nome e Cognome Stringhe di 10 caratteri senza duplicati.
  2. Nome e Cognome Stringhe di 50 caratteri senza duplicati.
  3. Nome e Cognome Stringhe di 10 caratteri con tutti i duplicati.

Si può vedere chiaramente che più lunghe sono le stringhe, peggiori sono le cose e che, al contrario, più duplicati diventano migliori. Poiché i duplicati menzionati in precedenza non influiscono sulla dimensione del piano memorizzato nella cache, quindi presumo che ci debba essere un processo di identificazione dei duplicati durante la costruzione dell'albero delle espressioni algebrizzato stesso.

Modifica

Un luogo in cui queste informazioni vengono sfruttate è mostrato da @Lieven qui

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

Perché in fase di compilazione può determinare che il Name la colonna non ha duplicati, salta l'ordinamento in base al 1/ (ID - ID) secondario espressione in fase di esecuzione (l'ordinamento nel piano ha solo un ORDER BY colonna) e non viene generato alcun errore di divisione per zero. Se vengono aggiunti duplicati alla tabella, l'operatore di ordinamento mostra due ordini per colonne e viene generato l'errore previsto.