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.
- Ciągi imienia i nazwiska o długości 10 znaków bez duplikatów.
- Ciągi imienia i nazwiska o długości 50 znaków bez duplikatów.
- 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.