sql >> Database >  >> RDS >> Database

Eenvoudige parametrering en triviale plannen - deel 3

Uitvoeringsplannen

Het is ingewikkelder dan je zou verwachten aan de hand van de informatie in uitvoeringsplannen als een SQL-instructie gebruikmaakt van eenvoudige parametrering . Het is geen verrassing dat zelfs zeer ervaren SQL Server-gebruikers dit vaak bij het verkeerde eind hebben, gezien de tegenstrijdige informatie die vaak aan ons wordt verstrekt.

Laten we eens kijken naar enkele voorbeelden van het gebruik van de Stack Overflow 2010-database op SQL Server 2019 CU 14, met databasecompatibiliteit ingesteld op 150.

Om te beginnen hebben we een nieuwe niet-geclusterde index nodig:

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

1. Eenvoudige parametrering toegepast

Deze eerste voorbeeldquery gebruikt eenvoudige parametrering :

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

De geschatte (pre-uitvoerings)plan heeft de volgende parametriseringsgerelateerde elementen:

Geschatte eigenschappen voor planparametrering

Let op de @1 parameter wordt overal geïntroduceerd, behalve de zoektekst die bovenaan wordt weergegeven.

De echte (post-uitvoering) plan heeft:

Eigenschappen voor de daadwerkelijke parametrering van het plan

Merk op dat het eigenschappenvenster nu de ParameterizedText heeft verloren element, terwijl informatie wordt verkregen over de runtime-waarde van de parameter. De geparameteriseerde zoektekst wordt nu bovenaan het venster weergegeven met '@1 ’ in plaats van ‘999’.

2. Eenvoudige parametrering niet toegepast

Dit tweede voorbeeld doet niet gebruik eenvoudige parametrering:

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

De geschatte plan toont:

Geschat plan zonder parameters

Deze keer is de parameter @1 ontbreekt in de Index Seek tooltip, maar de geparametriseerde tekst en andere parameterlijstelementen zijn hetzelfde als voorheen.

Laten we eens kijken naar de werkelijke uitvoeringsplan:

Eigenlijk niet-geparametriseerd plan

De resultaten zijn hetzelfde als de vorige geparametriseerde werkelijke plan, behalve nu de Index Seek tooltip geeft de niet-geparametreerde waarde '999' weer. De zoektekst die bovenaan wordt weergegeven, gebruikt de @1 parametermarkering. Het eigenschappenvenster gebruikt ook @1 en geeft de runtime-waarde van de parameter weer.

De query is geen geparametriseerde instructie ondanks al het bewijs van het tegendeel.

3. Parametrering mislukt

Mijn derde voorbeeld is ook niet geparametreerd door de server:

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

De geschatte plan is:

Geschatte parametrering van plan mislukt

Er is geen sprake van een @1 parameter nu overal, en de Parameterlijst gedeelte van het eigenschappenvenster ontbreekt.

De echte uitvoeringsplan is hetzelfde, dus ik zal niet de moeite nemen om het te laten zien.

4. Parallel geparametriseerd plan

Ik wil je nog een voorbeeld laten zien van parallellisme in het uitvoeringsplan. De lage geschatte kosten van mijn testquery's betekenen dat we de kostendrempel voor parallellisme moeten verlagen tot 1:

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

Het voorbeeld is deze keer wat complexer:

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

De geschatte uitvoeringsplan is:

Geschat parallel geparametriseerd plan

De querytekst bovenaan blijft ongeparametreerd, terwijl al het andere dat wel is. Er zijn nu twee parametermarkeringen, @1 en @2 , omdat eenvoudige parametrering vond twee geschikte letterlijke waarden.

De echte uitvoeringsplan volgt het patroon van voorbeeld 1:

Eigenlijk parallel geparametriseerd plan

De querytekst bovenaan is nu geparametriseerd en het eigenschappenvenster bevat runtime-parameterwaarden. Dit parallelle plan (met a Sorteren operator) is zeker geparametreerd door de server met behulp van eenvoudige parametrering .

Betrouwbare methoden

Er zijn redenen voor al het gedrag dat tot nu toe is getoond, en nog een paar meer. Ik zal proberen veel hiervan uit te leggen in het volgende deel van deze serie wanneer ik plancompilatie behandel.

Ondertussen is de situatie met showplan in het algemeen en SSMS in het bijzonder niet ideaal. Het is verwarrend voor mensen die hun hele loopbaan met SQL Server hebben gewerkt. Welke parametermarkeringen vertrouw je en welke negeer je?

Er zijn verschillende betrouwbare methoden om te bepalen of op een bepaalde instructie eenvoudige parametrisering is toegepast of niet.

Query-winkel

Ik zal beginnen met een van de handigste, de query store. Helaas is het niet altijd zo eenvoudig als je zou denken.

U moet de functie voor het opslaan van query's inschakelen voor de databasecontext waar de instructie wordt uitgevoerd en de OPERATION_MODE moet zijn ingesteld op READ_WRITE , waardoor de query store actief gegevens kan verzamelen.

Nadat aan deze voorwaarden is voldaan, bevat de showplan-uitvoer na uitvoering extra attributen, waaronder het StatementParameterizationType . Zoals de naam al doet vermoeden, bevat dit een code die het type parametrering beschrijft dat voor de instructie wordt gebruikt.

Het is zichtbaar in het SSMS-eigenschappenvenster wanneer het hoofdknooppunt van een plan is geselecteerd:

StatementParameterizationType

De waarden zijn gedocumenteerd in sys.query_store_query :

  • 0 – Geen
  • 1 – Gebruiker (expliciete parametrering)
  • 2 – Eenvoudige parametrering
  • 3 – Geforceerde parametrering

Dit voordelige kenmerk verschijnt alleen in SSMS wanneer een echte plan is aangevraagd en ontbreekt wanneer een geschatte plan is gekozen. Het is belangrijk om te onthouden dat het plan gecached moet zijn . Een geschatte . aanvragen plan van SSMS slaat het geproduceerde plan niet op in de cache (sinds SQL Server 2012).

Zodra het plan in de cache is opgeslagen, wordt het StatementParameterizationType verschijnt op de gebruikelijke plaatsen, waaronder via sys.dm_exec_query_plan .

U kunt ook vertrouwen op de andere plaatsen waar het type parametrering is vastgelegd in het queryarchief, zoals de query_parameterization_type_desc kolom in sys.query_store_query .

Een belangrijk voorbehoud. Wanneer de query OPERATION_MODE opslaat is ingesteld op READ_ONLY , het StatementParameterizationType attribuut is nog steeds ingevuld in SSMS actual plannen, maar het is altijd nul — een verkeerde indruk wekken dat de verklaring niet geparametriseerd was, terwijl dat wel zo zou kunnen zijn.

Als u tevreden bent met het inschakelen van het opslaan van query's, er zeker van bent dat het lezen-schrijven is en alleen naar plannen na de uitvoering in SSMS kijkt, zal dit voor u werken.

Standard Plan Predicates

De vraagtekst die bovenaan het grafische showplan-venster in SSMS wordt weergegeven, is niet betrouwbaar, zoals de voorbeelden hebben aangetoond. U kunt ook niet vertrouwen op de ParameterList weergegeven in de Eigenschappen venster wanneer het hoofdknooppunt van het plan is geselecteerd. De Geparameteriseerde Tekst kenmerk getoond voor geschatte alleen plannen is ook niet overtuigend.

U kunt echter vertrouwen op de eigenschappen die aan afzonderlijke planoperators zijn gekoppeld. De gegeven voorbeelden laten zien dat deze aanwezig zijn in de tooltips wanneer u over een operator zweeft.

Een predikaat met een parametermarkering zoals @1 of @2 geeft een geparametriseerd plan aan. De operators die het meest waarschijnlijk een parameter bevatten, zijn Index Scan , Index zoeken , en Filteren .

Predikaten met parametermarkeringen

Als de nummering begint met @1 , het gebruikt eenvoudige parametrering . Geforceerde parametrering begint met @0 . Ik moet vermelden dat het hier gedocumenteerde nummeringsschema op elk moment kan worden gewijzigd:

Waarschuwing wijzigen

Niettemin, dit is de methode die ik gebruik meestal om te bepalen of een plan onderhevig was aan parameterisatie aan de serverzijde. Het is over het algemeen snel en eenvoudig om een ​​plan visueel te controleren op predikaten die parametermarkeringen bevatten. Deze methode werkt ook voor beide soorten plannen, geschat en werkelijk .

Dynamische beheerobjecten

Er zijn verschillende manieren om de plancache en gerelateerde DMO's te doorzoeken om te bepalen of een instructie is geparametriseerd. Uiteraard werken deze zoekopdrachten alleen voor plannen in de cache, dus de instructie moet volledig zijn uitgevoerd, in de cache zijn opgeslagen en vervolgens om welke reden dan ook niet zijn verwijderd.

De meest directe benadering is om te zoeken naar een Adhoc plan met behulp van een exacte SQL-tekst die overeenkomt met de verklaring van interesse. De Adhoc plan is een shell met een ParameterizedPlanHandle als de instructie is geparametriseerd door de server. De planhandle wordt vervolgens gebruikt om de Prepared . te lokaliseren plan. Een Adhoc plan bestaat niet als de optimalisatie voor ad-hocworkloads is ingeschakeld en de betreffende instructie slechts één keer is uitgevoerd.

Dit type onderzoek leidt er vaak toe dat een aanzienlijke hoeveelheid XML wordt versnipperd en de volledige plancache minstens één keer wordt gescand. Het is ook gemakkelijk om de code verkeerd te krijgen, niet in de laatste plaats omdat plannen in de cache een hele batch beslaan. Een batch kan meerdere statements bevatten, die elk al dan niet geparametriseerd zijn. Niet alle DMO's werken met dezelfde granulariteit (batch of verklaring), waardoor het vrij eenvoudig is om los te komen.

Hieronder vindt u een efficiënte manier om interessante stellingen op te sommen, samen met planfragmenten voor alleen die individuele stellingen:

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;

Laten we ter illustratie een enkele batch draaien met de vier voorbeelden van eerder:

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

De output van de DMO-query is:

DMO-query-uitvoer

Dit bevestigt dat alleen voorbeelden 1 en 4 met succes zijn geparametriseerd.

Prestatietellers

Het is mogelijk om de prestatiemeteritems van SQL Statistics te gebruiken om een ​​gedetailleerd inzicht te krijgen in de parametreeractiviteit voor beide geschatte en werkelijk plannen. De gebruikte tellers hebben geen bereik per sessie, dus u moet een testinstantie gebruiken zonder andere gelijktijdige activiteit om nauwkeurige resultaten te krijgen.

Ik ga de parameterisatietellerinformatie aanvullen met gegevens uit de sys.dm_exec_query_optimizer_info DMO biedt ook statistieken over triviale plannen.

Enige voorzichtigheid is geboden om te voorkomen dat verklaringen die de tellerinformatie lezen, die tellers zelf wijzigen. Ik ga dit oplossen door een aantal tijdelijke opgeslagen procedures te maken:

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

Het script om een ​​bepaalde instructie te testen ziet er dan als volgt uit:

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;

Reageer op de SHOWPLAN_XML batches uit om de doelverklaring(en) uit te voeren en werkelijk . te krijgen plannen. Laat ze op hun plaats voor geschatte uitvoeringsplannen.

Het geheel uitvoeren zoals geschreven geeft de volgende resultaten:

Prestatieteller-testresultaten

Ik heb hierboven aangegeven waar de waarden veranderden bij het testen van voorbeeld 3.

De stijging van de teller 'triviaal plan' van 1050 naar 1051 laat zien dat er een triviaal plan is gevonden voor de testverklaring.

De tellers voor eenvoudige parametrering zijn met 1 verhoogd voor zowel pogingen als mislukkingen, wat aantoont dat SQL Server probeerde de instructie te parametriseren, maar faalde.

Einde van deel 3

In het volgende deel van deze serie zal ik de merkwaardige dingen die we hebben gezien uitleggen door te beschrijven hoe eenvoudige parametrering en triviale plannen interactie met het compilatieproces.

Als u uw kostendrempel voor parallellisme heeft gewijzigd om de voorbeelden uit te voeren, vergeet niet om het te resetten (de mijne was ingesteld op 50):

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

  1. SQL Server Express-back-updatabase | Hoe u SQL Express-back-up automatiseert en opschoont

  2. Meest gebruikte zoekopdrachten om Patch toegepast in Oracle-apps te vinden

  3. INITCAP() Functie in Oracle

  4. Hoe rijen binnen een partitie in SQL te rangschikken?