sql >> Databáze >  >> RDS >> Database

Jednoduchá parametrizace a triviální plány — 3. část

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;

  1. Další způsob zobrazení automatických aktualizací statistik

  2. Nejlepší způsoby použití příkazu SQL DELETE v tabulce SQL

  3. Jak můžete reprezentovat dědictví v databázi?

  4. Jak zjistit, zda je MySQLnd aktivním ovladačem?