Prováděcí plány
Pokud příkaz SQL používá jednoduchou parametrizaci, je to složitější, než byste čekali, že poznáte z informací uvedených v plánech provádění . Není překvapením, že i velmi zkušení uživatelé SQL Serveru mají tendenci se mýlit, vzhledem k protichůdným informacím, které nám často poskytují.
Podívejme se na několik příkladů s použitím databáze Stack Overflow 2010 na SQL Server 2019 CU 14, s kompatibilitou databáze nastavenou na 150.
Pro začátek budeme potřebovat nový neclusterovaný index:
CREATE INDEX [IX dbo.Users Reputation (DisplayName)] ON dbo.Users (Reputation) INCLUDE (DisplayName);
1. Použita jednoduchá parametrizace
Tento první příklad dotazu používá jednoduchou parametrizaci :
SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 999;
Odhad (předprováděcí) plán má následující prvky související s parametrizací:
Vlastnosti odhadované parametrizace plánu
Všimněte si @1
parametr je uveden všude kromě textu dotazu zobrazeného nahoře.
skutečné (po provedení) plán má:
Vlastnosti parametrizace skutečného plánu
Všimněte si, že okno vlastností nyní ztratilo ParameterizedText
prvek a zároveň získat informace o hodnotě parametru za běhu. Parametrizovaný text dotazu je nyní zobrazen v horní části okna s ‘@1
“ namísto „999“.
2. Jednoduchá parametrizace není použita
Tento druhý příklad ne použijte jednoduchou parametrizaci:
-- Projecting an extra column SELECT U.DisplayName, U.CreationDate -- NEW FROM dbo.Users AS U WHERE U.Reputation = 999;
Odhad plán ukazuje:
Odhadovaný neparametrizovaný plán
Tentokrát parametr @1
chybí v Hledání indexu tooltip, ale parametrizovaný text a další prvky seznamu parametrů jsou stejné jako dříve.
Podívejme se na skutečné prováděcí plán:
Skutečný neparametrizovaný plán
Výsledky jsou stejné jako předchozí parametrizované skutečné plán, kromě nyní Vyhledávání indexu tooltip zobrazuje neparametrizovanou hodnotu ‚999‘. Text dotazu zobrazený nahoře používá @1
značka parametru. Okno vlastností také používá @1
a zobrazí hodnotu parametru za běhu.
Dotaz není parametrizovaný příkaz navzdory všem důkazům o opaku.
3. Parametrizace se nezdařila
Můj třetí příklad je také ne parametrizované serverem:
-- LOWER function used SELECT U.DisplayName, LOWER(U.DisplayName) FROM dbo.Users AS U WHERE U.Reputation = 999;
Odhad plán je:
Parametrizace odhadovaného plánu selhala
Není zde žádná zmínka o @1
parametr kdekoli nyní a Seznam parametrů chybí část okna vlastností.
skutečné plán provádění je stejný, takže se nebudu obtěžovat ukazovat ho.
4. Paralelní parametrizovaný plán
Chci vám ukázat ještě jeden příklad použití paralelismu v prováděcím plánu. Nízká odhadovaná cena mých testovacích dotazů znamená, že musíme snížit prahovou hodnotu pro paralelismus na 1:
EXECUTE sys.sp_configure @configname = 'cost threshold for parallelism', @configvalue = 1; RECONFIGURE;
Příklad je tentokrát trochu složitější:
SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation >= 5 AND U.DisplayName > N'ZZZ' ORDER BY U.Reputation DESC;
Odhad plán provádění je:
Odhadovaný paralelní parametrizovaný plán
Text dotazu nahoře zůstává bez parametrů, zatímco vše ostatní je. Nyní existují dvě značky parametrů, @1
a @2
, protože jednoduchá parametrizace našel dvě vhodné doslovné hodnoty.
skutečné plán provádění se řídí vzorem příkladu 1:
Skutečný paralelní parametrizovaný plán
Text dotazu nahoře je nyní parametrizován a okno vlastností obsahuje hodnoty parametrů za běhu. Tento paralelní plán (s a Seřadit operátor) je jednoznačně parametrizován serverem pomocí jednoduché parametrizace .
Spolehlivé metody
Existují důvody pro všechna dosud uvedená chování a několik dalších. Mnohé z nich se pokusím vysvětlit v další části této série, když se budu věnovat kompilaci plánu.
Mezitím je situace se showplanem obecně, a SSMS zvláště, méně než ideální. Je to matoucí pro lidi, kteří s SQL Serverem pracují celou svou kariéru. Kterým značkám parametrů důvěřujete a které ignorujete?
Existuje několik spolehlivých metod, jak určit, zda na konkrétní příkaz byla úspěšně aplikována jednoduchá parametrizace nebo ne.
Obchod s dotazy
Začnu jedním z nejpohodlnějších, obchodem s dotazy. Bohužel to není vždy tak jednoduché, jak si dokážete představit.
Musíte povolit funkci úložiště dotazů pro kontext databáze kde je příkaz proveden a OPERATION_MODE
musí být nastaveno na READ_WRITE
, což umožňuje úložišti dotazů aktivně shromažďovat data.
Po splnění těchto podmínek bude výstup showplanu po spuštění obsahovat další atributy, včetně StatementParameterizationType . Jak název napovídá, obsahuje kód popisující typ parametrizace použité pro příkaz.
Je vidět v okně vlastností SSMS, když je vybrán kořenový uzel plánu:
StatementParameterizationType
Hodnoty jsou zdokumentovány v sys.query_store_query
:
- 0 – žádné
- 1 – Uživatel (explicitní parametrizace)
- 2 – Jednoduchá parametrizace
- 3 – Vynucená parametrizace
Tento výhodný atribut se v SSMS objeví pouze v případě, že skutečný plán je požadován a chybí, když je odhad plán je vybrán. Je důležité si uvědomit, že plán musí být uložen do mezipaměti . Žádost o odhad plán z SSMS neukládá vytvořený plán do mezipaměti (od SQL Server 2012).
Jakmile je plán uložen do mezipaměti, StatementParameterizationType se objeví na obvyklých místech, včetně prostřednictvím sys.dm_exec_query_plan
.
Můžete také důvěřovat dalším místům, kde je typ parametrizace zaznamenán v úložišti dotazů, jako je query_parameterization_type_desc
ve sloupci sys.query_store_query
.
Jedno důležité upozornění. Když dotaz ukládá OPERATION_MODE
je nastaveno na READ_ONLY
, StatementParameterizationType atribut je stále vyplněn v SSMS aktuální plány – ale je to vždy nulové —vyvolávání mylného dojmu, že příkaz nebyl parametrizován, i když mohl být.
Pokud rádi povolíte úložiště dotazů, jste si jisti, že jde o čtení i zápis a díváte se pouze na plány po spuštění v SSMS, bude to fungovat pro vás.
Standardní predikáty plánu
Text dotazu zobrazený v horní části okna grafického plánu show v SSMS není spolehlivý, jak ukázaly příklady. Nelze se spolehnout ani na ParameterList zobrazené v Vlastnosti okno, když je vybrán kořenový uzel plánu. ParameterizedText atribut zobrazený pro odhad pouze plány také není přesvědčivé.
Můžete se však spolehnout na vlastnosti spojené s jednotlivými operátory plánu. Uvedené příklady ukazují, že jsou uvedeny v popisech když najedete na operátor.
Predikát obsahující značku parametru jako @1
nebo @2
označuje parametrizovaný plán. Operátory, které s největší pravděpodobností obsahují parametr, jsou Index Scan , Hledání indexu a Filtrovat .
Predikáty se značkami parametrů
Pokud číslování začíná @1
, používá jednoduchou parametrizaci . Vynucená parametrizace začíná @0
. Měl bych zmínit, že zde zdokumentované schéma číslování se může kdykoli změnit:
Upozornění na změnu
Nicméně toto je metoda, kterou používám nejčastěji k určení, zda plán podléhal parametrizaci na straně serveru. Obecně je rychlé a snadné vizuálně zkontrolovat plán pro predikáty obsahující značky parametrů. Tato metoda také funguje pro oba typy plánů, odhad a skutečné .
Objekty dynamické správy
Existuje několik způsobů, jak dotazovat mezipaměť plánu a související DMO, abyste zjistili, zda byl příkaz parametrizován. Tyto dotazy přirozeně fungují pouze na plánech v mezipaměti, takže příkaz musí být proveden do konce, uložen do mezipaměti a nesmí být následně z jakéhokoli důvodu vyřazen.
Nejpřímější přístup je hledat Adhoc plán pomocí přesné textové shody SQL s prohlášením o zájmu. Adhoc plán bude shell obsahující ParameterizedPlanHandle pokud je příkaz parametrizován serverem. Rukojeť plánu se pak použije k nalezení Připraveno plán. Adhoc plán nebude existovat, pokud je povolena optimalizace pro pracovní zátěže ad hoc a příslušný příkaz byl proveden pouze jednou.
Tento typ dotazu často končí skartováním značného množství XML a skenováním celé mezipaměti plánu alespoň jednou. Je také snadné dostat kód špatně, v neposlední řadě proto, že plány v mezipaměti pokrývají celou dávku. Dávka může obsahovat více příkazů, z nichž každý může nebo nemusí být parametrizován. Ne všechny DMO fungují se stejnou granularitou (dávka nebo výpis), takže je docela snadné se odlepit.
Efektivní způsob, jak vypsat seznam zajímavých výpisů spolu s fragmenty plánu pouze pro tyto jednotlivé výpisy, je uveden níže:
SELECT StatementText = SUBSTRING(T.[text], 1 + (QS.statement_start_offset / 2), 1 + ((QS.statement_end_offset - QS.statement_start_offset) / 2)), IsParameterized = IIF(T.[text] LIKE N'(%', 'Yes', 'No'), query_plan = TRY_CONVERT(xml, P.query_plan) FROM sys.dm_exec_query_stats AS QS CROSS APPLY sys.dm_exec_sql_text (QS.[sql_handle]) AS T CROSS APPLY sys.dm_exec_text_query_plan ( QS.plan_handle, QS.statement_start_offset, QS.statement_end_offset) AS P WHERE -- Statements of interest T.[text] LIKE N'%DisplayName%Users%' -- Exclude queries like this one AND T.[text] NOT LIKE N'%sys.dm%' ORDER BY QS.last_execution_time ASC, QS.statement_start_offset ASC;
Pro ilustraci spustíme jednu dávku obsahující čtyři příklady z dříve:
ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE; GO -- Example 1 SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 999; -- Example 2 SELECT U.DisplayName, U.CreationDate FROM dbo.Users AS U WHERE U.Reputation = 999; -- Example 3 SELECT U.DisplayName, LOWER(U.DisplayName) FROM dbo.Users AS U WHERE U.Reputation = 999; -- Example 4 SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation >= 5 AND U.DisplayName > N'ZZZ' ORDER BY U.Reputation DESC; GO
Výstup dotazu DMO je:
Výstup dotazu DMO
To potvrzuje, že pouze příklady 1 a 4 byly úspěšně parametrizovány.
Počítadla výkonu
Je možné použít čítače výkonu SQL Statistics k získání podrobného přehledu o aktivitě parametrizace pro obě odhadované a skutečné plány. Použité počítadla nejsou rozsahem na relaci, takže k získání přesných výsledků budete muset použít testovací instanci bez jiné souběžné aktivity.
Doplním informace o čítači parametrizace o data z sys.dm_exec_query_optimizer_info
DMO poskytovat statistiky i o triviálních plánech.
Je zapotřebí určité opatrnosti, aby se zabránilo tomu, že příkazy čtoucí informace počítadla samotné tyto počítadla modifikovaly. Budu to řešit vytvořením několika dočasných uložených procedur:
CREATE PROCEDURE #TrivialPlans AS SET NOCOUNT ON; SELECT OI.[counter], OI.occurrence FROM sys.dm_exec_query_optimizer_info AS OI WHERE OI.[counter] = N'trivial plan'; GO CREATE PROCEDURE #PerfCounters AS SET NOCOUNT ON; SELECT PC.[object_name], PC.counter_name, PC.cntr_value FROM sys.dm_os_performance_counters AS PC WHERE PC.counter_name LIKE N'%Param%';
Skript pro testování konkrétního příkazu pak vypadá takto:
ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE; GO EXECUTE #PerfCounters; EXECUTE #TrivialPlans; GO SET SHOWPLAN_XML ON; GO -- The statement(s) under test: -- Example 3 SELECT U.DisplayName, LOWER(U.DisplayName) FROM dbo.Users AS U WHERE U.Reputation = 999; GO SET SHOWPLAN_XML OFF; GO EXECUTE #TrivialPlans; EXECUTE #PerfCounters;
Komentář SHOWPLAN_XML
dá se spustit cílový příkaz(y) a získat aktuální plány. Nechte je na místě pro odhad prováděcí plány.
Spuštění celé věci tak, jak je napsáno, dává následující výsledky:
Výsledky testu počítadla výkonu
Výše jsem zdůraznil, kde se hodnoty změnily při testování příkladu 3.
Zvýšení počítadla „triviálního plánu“ z 1050 na 1051 ukazuje, že pro testovací prohlášení byl nalezen triviální plán.
Jednoduché čítače parametrizace se zvýšily o 1 pro pokusy i selhání, což ukazuje, že SQL Server se pokusil parametrizovat příkaz, ale selhal.
Konec 3. části
V další části této série vysvětlím kuriózní věci, které jsme viděli, popisem toho, jak jednoduchá parametrizace a triviální plány interagovat s procesem kompilace.
Pokud jste změnili prah nákladů pro paralelismus Chcete-li spustit příklady, nezapomeňte jej resetovat (můj byl nastaven na 50):
EXECUTE sys.sp_configure @configname = 'cost threshold for parallelism', @configvalue = 50; RECONFIGURE;