Dodatek: SQL Server 2012 wykazuje pewną poprawę wydajności w tym obszarze, ale wydaje się, że nie rozwiązuje określonych problemów opisanych poniżej. Najwyraźniej 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 parametryzowanych automatycznie), więc czas analizy / kompilacji dla nich powinien być minimalny.
Pomyślałem, że przyjrzę się temu nieco bardziej, więc skonfigurowałem pętlę ( skrypt ) i spróbowałem dostosować liczbę VALUES
klauzul i zarejestrować czas kompilacji.
Następnie podzieliłem czas kompilacji przez liczbę wierszy, aby uzyskać średni czas kompilacji na klauzulę. Wyniki poniżej
Aż do 250 VALUES
klauzul czas kompilacji / liczba klauzul ma niewielką tendencję wzrostową, ale nic zbyt dramatycznego.
Ale potem następuje nagła zmiana.
Ta sekcja 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 wzrastał liniowo, nagle spada, ale CompileTime zwiększa się siedmiokrotnie, a CompileMemory rośnie. Jest to punkt odcięcia między planem sparametryzowanym automatycznie (z 1000 parametrów) a niesparametryzowanym. Od tego czasu wydaje się, że staje się on liniowo mniej wydajny (pod względem liczby klauzul wartości przetwarzanych w danym czasie).
Nie wiem, dlaczego tak powinno być. Przypuszczalnie podczas kompilowania planu dla określonych wartości literalnych musi wykonać jakąś czynność, która nie jest skalowana liniowo (np. Sortowanie).
Wydaje się, że nie wpływa to na rozmiar zbuforowanego planu zapytań, gdy próbowałem kwerendy składającej się wyłącznie z zduplikowanych wierszy i nie wpływa na kolejność danych wyjściowych tabeli stałych (i gdy wstawiasz do sterty czas spędzony na sortowaniu i tak byłoby bezcelowe, nawet gdyby tak było).
Co więcej, jeśli indeks klastrowy zostanie dodany do tabeli, plan nadal pokazuje jawny krok sortowania, więc nie wydaje się, aby sortował w czasie kompilacji, aby uniknąć sortowania w czasie wykonywania.
Próbowałem przyjrzeć się temu w debugerze, ale symbole publiczne mojej wersji SQL Server 2008 nie wydają się być dostępne, więc zamiast tego musiałem przyjrzeć się równoważnej UNION ALL
konstrukcji w SQL Server 2005.
Poniżej znajduje się typowy ślad stosu
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
Więc opuszczenie nazw w śladzie stosu wydaje się spędzać dużo czasu na porównywaniu ciągów.
Ten artykuł z bazy wiedzy wskazuje, że DeriveNormalizedGroupProperties
jest on powiązany z tak zwanym etapem normalizacji przetwarzania zapytań
Ten etap jest teraz nazywany wiązaniem lub algebrizacją i pobiera dane wyjściowe drzewa parsowania wyrażenia z poprzedniego etapu analizy i generuje algebrizowane drzewo wyrażeń (drzewo procesora zapytań), aby przejść do optymalizacji (w tym przypadku trywialna optymalizacja planu) [ref] .
Spróbowałem jeszcze jednego eksperymentu ( skrypt ), który polegał na ponownym uruchomieniu oryginalnego testu, ale przyjrzałem się trzem różnym przypadkom.
- Imię i nazwisko Ciągi o długości 10 znaków bez duplikatów.
- Imię i nazwisko Ciągi o długości 50 znaków bez duplikatów.
- Imię i nazwisko Ciągi o długości 10 znaków ze wszystkimi duplikatami.
Wyraźnie widać, że im dłuższe struny, tym gorsze rzeczy, i odwrotnie, im więcej duplikatów, tym lepsze rzeczy. Jak wspomniano wcześniej, duplikaty nie wpływają na rozmiar planu w pamięci podręcznej, więc zakładam, że musi istnieć proces podwójnej identyfikacji podczas tworzenia samego algebrizowanego drzewa wyrażeń.
Edytować
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że określić, że Name
kolumna nie ma duplikatów, pomija porządkowanie według 1/ (ID - ID)
wyrażenia pomocniczego w czasie wykonywania (sortowanie w planie ma tylko jedną ORDER BY
kolumnę) 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 zgłaszany jest oczekiwany błąd.