Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Wiele instrukcji INSERT vs. pojedyncza INSERT z wieloma VALUES

Dodanie: SQL Server 2012 wykazuje pewną poprawę wydajności w tym obszarze, ale wydaje się, że nie rozwiązuje problemów wymienionych poniżej. Powinno to zostać naprawione w następnej głównej wersji po SQL Server 2012!

Twój plan pokazuje, że pojedyncze wstawki używają sparametryzowanych procedur (prawdopodobnie automatycznie sparametryzowanych), więc czas ich analizy/kompilacji powinien być minimalny.

Pomyślałem, że przyjrzę się temu nieco dokładniej, więc ustaw pętlę (skrypt) i spróbuj dostosować liczbę VALUES klauzul i zapisywanie czasu kompilacji.

Następnie podzieliłem czas kompilacji przez liczbę wierszy, aby uzyskać średni czas kompilacji na klauzulę. Wyniki są poniżej

Do 250 VALUES klauzule przedstawiają czas kompilacji / liczbę klauzul ma niewielką tendencję wzrostową, ale nie jest to zbyt dramatyczne.

Ale potem następuje nagła zmiana.

Ta część danych jest pokazana poniżej.

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

Rozmiar planu w pamięci podręcznej, który rósł liniowo, nagle spada, ale CompileTime zwiększa się 7-krotnie, a CompileMemory rośnie. Jest to punkt odcięcia między planem automatycznie sparametryzowanym (z 1000 parametrów) a niesparametryzowanym. Następnie wydaje się, że staje się liniowo mniej wydajny (pod względem liczby klauzul wartości przetwarzanych w danym czasie).

Nie wiem, dlaczego tak powinno być. Przypuszczalnie, gdy kompiluje plan dla określonych wartości dosłownych, musi wykonać jakąś czynność, która nie skaluje się liniowo (np. sortowanie).

Wygląda na to, że nie ma to wpływu na rozmiar buforowanego planu zapytań, kiedy próbowałem zapytania składającego sięw całości z duplikatów wierszy i nie ma to wpływu na kolejnośćwyjścia tabeli stałych (i podczas wstawiania do sterty czas spędzony na sortowaniu i tak byłoby bezcelowe, nawet gdyby tak było).

Co więcej, jeśli do tabeli zostanie dodany indeks klastrowy, plan nadal pokazuje jawny krok sortowania, więc nie wydaje się, aby sortowanie odbywało się w czasie kompilacji, aby uniknąć sortowania w czasie wykonywania.

Próbowałem przyjrzeć się temu w debuggerze, ale publiczne symbole mojej wersji SQL Server 2008 nie wydają się być dostępne, więc zamiast tego musiałem spojrzeć na odpowiednik UNION ALL budowa w SQL Server 2005.

Typowy ślad stosu znajduje się poniżej

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   

Tak więc pomijając nazwy w śladzie stosu, wydaje się, że zajmuje dużo czasu porównywanie ciągów.

Ten artykuł KB wskazuje, że DeriveNormalizedGroupProperties wiąże się z etapem normalizacji przetwarzania zapytań

Ten etap jest teraz nazywany wiązaniem lub algebraizowaniem i pobiera dane wyjściowe drzewa analizy wyrażenia z poprzedniego etapu analizy i generuje zalgebrowane drzewo wyrażeń (drzewo procesora zapytań), aby przejść do optymalizacji (w tym przypadku optymalizacji planu trywialnego) [ref].

Spróbowałem jeszcze jednego eksperymentu (Skryptu), który polegał na ponownym uruchomieniu oryginalnego testu, ale przyjrzałem się trzem różnym przypadkom.

  1. Ciągi imienia i nazwiska o długości 10 znaków bez duplikatów.
  2. Ciągi imienia i nazwiska o długości 50 znaków bez duplikatów.
  3. Ciągi imienia i nazwiska o długości 10 znaków ze wszystkimi duplikatami.

Widać wyraźnie, że im dłuższe sznurki, tym gorsze rzeczy i odwrotnie, im więcej duplikatów, tym lepsze rzeczy. Jak wspomniano wcześniej, duplikaty nie wpływają na rozmiar buforowanego planu, więc zakładam, że podczas konstruowania samego drzewa wyrażeń algebraicznych musi istnieć proces identyfikacji duplikatów.

Edytuj

Jedno miejsce, w którym wykorzystuje się te informacje, pokazuje @Lieven tutaj

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

Ponieważ w czasie kompilacji można określić, że Name kolumna nie ma duplikatów, pomija kolejność według drugorzędnego 1/ (ID - ID) wyrażenie w czasie wykonywania (sortowanie w planie ma tylko jedno ORDER BY kolumna) i nie jest zgłaszany błąd dzielenia przez zero. Jeśli do tabeli zostaną dodane duplikaty, operator sortowania pokazuje dwie kolejności według kolumn i pojawia się oczekiwany błąd.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. uzyskaj nowy identyfikator rekordu SQL

  2. Jakie kolumny generalnie tworzą dobre indeksy?

  3. Uruchamianie programu SQL Server 2014 na maszynie wirtualnej platformy Azure

  4. Scal nakładające się interwały dat

  5. Jak wyodrębnić lub przekonwertować dane czasu z ciągu w SQL Server