sql >> Database teknologi >  >> RDS >> Database

Simpel parametrering og trivielle planer — del 3

Udførelsesplaner

Det er mere kompliceret, end du måske forventer at se ud fra oplysningerne i udførelsesplaner, hvis en SQL-sætning bruger simpel parameterisering . Det er ingen overraskelse, at selv meget erfarne SQL Server-brugere har en tendens til at tage fejlen, i betragtning af de modstridende oplysninger, der ofte leveres til os.

Lad os se på nogle eksempler på brug af Stack Overflow 2010-databasen på SQL Server 2019 CU 14, med databasekompatibilitet sat til 150.

For at begynde skal vi bruge et nyt ikke-klynget indeks:

CREATE INDEX [IX dbo.Users Reputation (DisplayName)] 
ON dbo.Users (Reputation) 
INCLUDE (DisplayName);

1. Simpel parametrering anvendt

Dette første eksempelforespørgsel bruger simpel parameterisering :

SELECT U.DisplayName 
FROM dbo.Users AS U 
WHERE U.Reputation = 999;

Den estimerede (pre-execution) planen har følgende parameteriseringsrelaterede elementer:

Estimerede planparameteregenskaber

Læg mærke til @1 parameter introduceres overalt undtagen forespørgselsteksten vist øverst.

Den faktiske (efter-udførelse) planen har:

Faktiske planparameteregenskaber

Bemærk, at egenskabsvinduet nu har mistet ParameterizedText element, mens du får information om parameterens runtime-værdi. Den parametriserede forespørgselstekst vises nu øverst i vinduet med '@1 ' i stedet for '999'.

2. Simpel parametrering ikke anvendt

Dette andet eksempel gør ikke brug simpel parameterisering:

-- Projecting an extra column
SELECT 
    U.DisplayName, 
    U.CreationDate -- NEW
FROM dbo.Users AS U 
WHERE 
    U.Reputation = 999;

Den estimerede planen viser:

Estimeret ikke-parameteriseret plan

Denne gang parameteren @1 mangler i indekssøgningen værktøjstip, men den parametriserede tekst og andre parameterlisteelementer er de samme som før.

Lad os se på det faktiske udførelsesplan:

Faktisk ikke-parameteriseret plan

Resultaterne er de samme som de tidligere parametriserede faktiske plan, undtagen nu Index Seek værktøjstip viser den ikke-parametriserede værdi '999'. Forespørgselsteksten vist øverst bruger @1 parameter markør. Egenskabsvinduet bruger også @1 og viser køretidsværdien for parameteren.

Forespørgslen er ikke en parametriseret sætning trods alle beviser for det modsatte.

3. Parametrering mislykkedes

Mit tredje eksempel er også ikke parametreret af serveren:

-- LOWER function used
SELECT 
    U.DisplayName, 
    LOWER(U.DisplayName)
FROM dbo.Users AS U 
WHERE 
    U.Reputation = 999;

Den estimerede planen er:

Estimeret planparametrering mislykkedes

Der er ingen omtale af en @1 parameter hvor som helst nu, og Parameterlisten sektionen af ​​egenskabsvinduet mangler.

Den faktiske udførelsesplanen er den samme, så jeg gider ikke vise den.

4. Parallel parametreret plan

Jeg vil gerne vise dig endnu et eksempel, hvor du bruger parallelitet i udførelsesplanen. De lave estimerede omkostninger ved mine testforespørgsler betyder, at vi er nødt til at sænke omkostningstærsklen for parallelitet til 1:

EXECUTE sys.sp_configure
    @configname = 'cost threshold for parallelism',
    @configvalue = 1;
RECONFIGURE;

Eksemplet er lidt mere komplekst denne gang:

SELECT 
    U.DisplayName 
FROM dbo.Users AS U 
WHERE 
    U.Reputation >= 5 
    AND U.DisplayName > N'ZZZ' 
ORDER BY 
    U.Reputation DESC;

Den estimerede udførelsesplanen er:

Estimeret parallel parameteriseret plan

Forespørgselsteksten henover toppen forbliver uparameteriseret, mens alt andet er det. Der er nu to parametermarkører, @1 og @2 , fordi simpel parameterisering fundet to passende bogstavelige værdier.

Den faktiske udførelsesplan følger mønsteret i eksempel 1:

Faktisk parallel parameteriseret plan

Forespørgselsteksten øverst er nu parametriseret, og egenskabsvinduet indeholder runtime-parameterværdier. Denne parallelle plan (med en Sortér operator) er bestemt parametriseret af serveren ved hjælp af simpel parameterisering .

Plidelige metoder

Der er grunde til al den adfærd, der er vist indtil videre, og et par flere derudover. Jeg vil forsøge at forklare mange af disse i den næste del af denne serie, når jeg dækker plansamling.

I mellemtiden er situationen med showplan generelt, og SSMS i særdeleshed, mindre end ideel. Det er forvirrende for folk, der har arbejdet med SQL Server hele deres karriere. Hvilke parametermarkører stoler du på, og hvilke ignorerer du?

Der er adskillige pålidelige metoder til at afgøre, om en bestemt sætning havde en simpel parameterisering med succes eller ej.

Forespørgselsbutik

Jeg starter med en af ​​de mest bekvemme, forespørgselsbutikken. Desværre er det ikke altid så ligetil, som du måske forestiller dig.

Du skal aktivere funktionen forespørgselslager for databasekonteksten hvor sætningen udføres og OPERATION_MODE skal indstilles til READ_WRITE , hvilket gør det muligt for forespørgselslageret aktivt at indsamle data.

Når disse betingelser er opfyldt, indeholder post-udførelse showplan-output ekstra attributter, herunder StatementParameterizationType . Som navnet antyder, indeholder dette en kode, der beskriver typen af ​​parameterisering, der bruges til sætningen.

Det er synligt i SSMS-egenskabsvinduet, når rodknudepunktet for en plan er valgt:

StatementParameterizationType

Værdierne er dokumenteret i sys.query_store_query :

  • 0 – Ingen
  • 1 – Bruger (eksplicit parameterisering)
  • 2 – Simpel parameterisering
  • 3 – Tvunget parametrering

Denne fordelagtige egenskab vises kun i SSMS, når en faktisk plan er anmodet og mangler, når en estimeret planen er valgt. Det er vigtigt at huske, at planen skal være cache . Anmoder om en estimeret plan fra SSMS cachelagrer ikke den producerede plan (siden SQL Server 2012).

Når planen er cachelagret, vises StatementParameterizationType vises på de sædvanlige steder, herunder via sys.dm_exec_query_plan .

Du kan også stole på de andre steder, parameteriseringstypen er registreret i forespørgselslageret, såsom query_parameterization_type_desc kolonne i sys.query_store_query .

En vigtig advarsel. Når forespørgslen lagrer OPERATION_MODE er indstillet til READ_ONLY , StatementParameterizationType attribut er stadig udfyldt i SSMS faktisk planer – men det er altid nul — hvilket giver et falsk indtryk, at udsagnet ikke var parametriseret, når det godt kunne have været det.

Hvis du er glad for at aktivere forespørgselslageret, er sikker på, at det er læse-skrive, og kun ser på efterudførelsesplaner i SSMS, vil dette fungere for dig.

Standardplanprædikater

Forespørgselsteksten, der vises øverst i det grafiske showplan-vindue i SSMS, er ikke pålidelig, som eksemplerne har vist. Du kan heller ikke stole på ParameterList vises i Egenskaber vindue, når rodknudepunktet for planen er valgt. ParameterizedText attribut vist for estimeret kun planer er heller ikke afgørende.

Du kan dog stole på de ejendomme, der er knyttet til individuelle planoperatører. De givne eksempler viser, at disse er til stede i værktøjstip når du svæver over en operatør.

Et prædikat, der indeholder en parametermarkør som @1 eller @2 angiver en parametriseret plan. De operatører, der mest sandsynligt indeholder en parameter, er Index Scan , Indekssøgning og Filter .

Prdikater med parametermarkører

Hvis nummereringen starter med @1 , den bruger simpel parameterisering . Tvungen parameterisering begynder med @0 . Jeg bør nævne, at nummereringsskemaet, der er dokumenteret her, kan ændres til enhver tid:

Skift advarsel

Ikke desto mindre er dette metode jeg bruger oftest for at afgøre, om en plan var underlagt parametrering på serversiden. Det er generelt hurtigt og nemt at kontrollere en plan visuelt for prædikater, der indeholder parametermarkører. Denne metode virker også for begge typer planer, estimeret og faktisk .

Dynamiske styringsobjekter

Der er flere måder at forespørge planens cache og relaterede DMO'er på for at afgøre, om en sætning blev parameteriseret. Disse forespørgsler virker naturligvis kun på planer i cachen, så erklæringen skal være udført til fuldførelse, cachelagret og ikke efterfølgende smidt ud af nogen grund.

Den mest direkte tilgang er at lede efter en Adhoc planlægge ved hjælp af en nøjagtig SQL-tekstmæssig match til interesseerklæringen. Adhoc planen vil være en skal indeholdende et ParameterizedPlanHandle hvis sætningen er parametriseret af serveren. Planhåndtaget bruges derefter til at finde Forberedt plan. En Adhoc planen eksisterer ikke, hvis optimering til ad hoc-arbejdsbelastninger er aktiveret, og den pågældende erklæring kun er udført én gang.

Denne type forespørgsel ender ofte med at makulere en betydelig mængde XML og scanne hele planens cache mindst én gang. Det er også nemt at få koden forkert, ikke mindst fordi planer i cache dækker en hel batch. En batch kan indeholde flere sætninger, som hver kan være parametriseret eller ikke. Ikke alle DMO'er arbejder med samme granularitet (batch eller erklæring), hvilket gør det ret nemt at komme ud af det.

En effektiv måde at liste interesseerklæringer sammen med planfragmenter for netop disse individuelle udsagn er vist nedenfor:

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;

For at illustrere det, lad os køre en enkelt batch, der indeholder de fire eksempler fra tidligere:

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

Outputtet af DMO-forespørgslen er:

DMO-forespørgselsoutput

Dette bekræfter, at kun eksempel 1 og 4 blev parameteriseret med succes.

Performancetællere

Det er muligt at bruge SQL Statistics ydeevnetællere til at få et detaljeret indblik i parameteriseringsaktivitet for både estimerede og faktisk planer. De anvendte tællere er ikke scoped per-session, så du bliver nødt til at bruge en testinstans uden anden samtidig aktivitet for at få nøjagtige resultater.

Jeg vil supplere parametertælleroplysningerne med data fra sys.dm_exec_query_optimizer_info DMO til at levere statistik om trivielle planer også.

Der er behov for en vis omhu for at forhindre, at udsagn, der læser tællerinformationen, selv ændrer disse tællere. Jeg vil løse dette ved at oprette et par midlertidige lagrede procedurer:

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%';

Scriptet til at teste et bestemt udsagn ser så således ud:

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;

Kommenter SHOWPLAN_XML batches ud for at køre målsætningerne og få faktiske planer. Lad dem være på plads til estimeret udførelsesplaner.

At køre det hele som skrevet giver følgende resultater:

Testresultater for ydeevnetæller

Jeg har fremhævet ovenfor, hvor værdierne ændrede sig ved testning af eksempel 3.

Stigningen i "trivial plan"-tælleren fra 1050 til 1051 viser, at der blev fundet en triviel plan for testerklæringen.

De simple parameteriseringstællere steg med 1 for både forsøg og fejl, hvilket viser, at SQL Server forsøgte at parametrere sætningen, men mislykkedes.

Slut på del 3

I den næste del af denne serie vil jeg forklare de nysgerrige ting, vi har set, ved at beskrive, hvordan simpel parameterisering og trivielle planer interagere med kompileringsprocessen.

Hvis du ændrede din omkostningstærskel for parallelitet For at køre eksemplerne skal du huske at nulstille det (min var sat til 50):

EXECUTE sys.sp_configure
    @configname = 'cost threshold for parallelism',
    @configvalue = 50;
RECONFIGURE;

  1. Hvordan kan jeg oprette en begrænsning for at kontrollere, om en e-mail er gyldig i postgres?

  2. Hvordan indstiller man sortering for en forbindelse i SQL Server?

  3. SQL Server Error 206:Operand type sammenstød

  4. Fjern dublet fra en tabel