sql >> Databasteknik >  >> RDS >> Database

Parameter Sniffing, Inbäddning och ÅTERKOMPILERING

Parametersniffning

Frågeparameterisering främjar återanvändning av cachade exekveringsplaner, och undviker därigenom onödiga kompileringar och minskar antalet ad-hoc-frågor i planens cache.

Dessa är alla bra saker, förutsatt frågan som parametriseras borde verkligen använda samma cachade exekveringsplan för olika parametervärden. En exekveringsplan som är effektiv för ett parametervärde kanske inte vara ett bra val för andra möjliga parametervärden.

När parametersniffning är aktiverat (standard), väljer SQL Server en exekveringsplan baserat på de specifika parametervärden som finns vid kompileringstillfället. Det implicita antagandet är att parametriserade satser oftast exekveras med de vanligaste parametervärdena. Detta låter rimligt nog (även självklart) och det fungerar faktiskt ofta bra.

Ett problem kan uppstå när en automatisk omkompilering av den cachade planen inträffar. En omkompilering kan utlösas av alla möjliga orsaker, till exempel för att ett index som används av den cachade planen har tagits bort (en riktighet omkompilering) eller för att statistisk information har ändrats (en optimalitet kompilera om).

Oavsett den exakta orsaken av planomkompileringen finns det en chans att en atypisk värde skickas som en parameter när den nya planen genereras. Detta kan resultera i en ny cachad plan (baserat på det sniffade atypiska parametervärdet) som inte är bra för de flesta körningar för vilka den kommer att återanvändas.

Det är inte lätt att förutsäga när en viss utförandeplan kommer att kompileras om (till exempel eftersom statistiken har förändrats tillräckligt) vilket resulterar i en situation där en återanvändbar plan av god kvalitet plötsligt kan ersättas av en helt annan plan optimerad för atypiska parametervärden.

Ett sådant scenario inträffar när det atypiska värdet är mycket selektivt, vilket resulterar i en plan som är optimerad för ett litet antal rader. Sådana planer kommer ofta att använda entrådad exekvering, kapslade loops-kopplingar och uppslagningar. Allvarliga prestandaproblem kan uppstå när denna plan återanvänds för olika parametervärden som genererar ett mycket större antal rader.

Inaktivera parametersniffning

Parametersniffning kan inaktiveras med den dokumenterade spårningsflaggan 4136. Spårningsflaggan stöds även för per fråga använd via QUERYTRACEON frågetips. Båda gäller från SQL Server 2005 Service Pack 4 och framåt (och något tidigare om du tillämpar kumulativa uppdateringar av Service Pack 3).

Från och med SQL Server 2016 kan parametersniffning även inaktiveras på databasnivå , med hjälp av PARAMETER_SNIFFING argument till ALTER DATABASE SCOPED CONFIGURATION .

När parametersniffning är inaktiverad använder SQL Server genomsnittlig distribution statistik för att välja en genomförandeplan.

Detta låter också som ett rimligt tillvägagångssätt (och kan hjälpa till att undvika situationen där planen är optimerad för ett ovanligt selektivt parametervärde), men det är inte heller en perfekt strategi:En plan som är optimerad för ett "genomsnittligt" värde kan mycket väl hamna i allvarligt suboptimal för de vanliga parametervärdena.

Överväg en exekveringsplan som innehåller minneskrävande operatörer som sortering och hash. Eftersom minnet är reserverat innan exekveringen av sökfrågan startar, kan en parametriserad plan baserad på genomsnittliga distributionsvärden spilla till tempdb för vanliga parametervärden som producerar mer data än vad optimeraren förväntade sig.

Minnesreservationer kan vanligtvis inte växa under exekvering av en fråga, oavsett hur mycket ledigt minne servern kan ha. Vissa applikationer drar nytta av att stänga av parametersniffning (se detta arkivinlägg av Dynamics AX Performance Team för ett exempel).

För de flesta arbetsbelastningar är fel lösning att inaktivera parametersniffning helt och hållet , och kan till och med vara en katastrof. Parametersniffning är en heuristisk optimering:Det fungerar bättre än att använda medelvärden på de flesta system, för det mesta.

Frågetips

SQL Server tillhandahåller en rad frågetips och andra alternativ för att justera beteendet för parametersniffning:

  • OPTIMIZE FOR (@parameter = value) frågetips bygger en återanvändbar plan baserat på ett specifikt värde.
  • OPTIMIZE FOR (@parameter UNKNOWN) använder genomsnittlig distributionsstatistik för en viss parameter.
  • OPTIMIZE FOR UNKNOWN använder genomsnittlig distribution för alla parametrar (samma effekt som spårningsflagga 4136).
  • WITH RECOMPILE alternativet lagrad procedur kompilerar en ny procedurplan för varje exekvering.
  • OPTION (RECOMPILE) frågetips sammanställer en ny plan för ett individuellt uttalande.

Den gamla tekniken med "parameterdöljning" (att tilldela procedurparametrar till lokala variabler och hänvisa till variablerna istället) har samma effekt som att specificera OPTIMIZE FOR UNKNOWN . Det kan vara användbart på instanser tidigare än SQL Server 2008 (OPTIMIZE FOR tipset var nytt för 2008).

Man kan hävda att varje parameteriserad sats bör kontrolleras för känslighet för parametervärden, och antingen lämnas ifred (om standardbeteendet fungerar bra) eller uttryckligen antydas med hjälp av något av alternativen ovan.

Detta görs sällan i praktiken, bland annat eftersom att utföra en omfattande analys för alla möjliga parametervärden kan vara tidskrävande och kräver ganska avancerade färdigheter.
Oftast görs ingen sådan analys och parameterkänslighetsproblem åtgärdas som och när de förekommer i produktionen.

Denna brist på tidigare analys är förmodligen en huvudorsak till att parametersnuffning har ett dåligt rykte. Det lönar sig att vara medveten om potentialen för att problem kan uppstå och att utföra åtminstone en snabb analys av påståenden som sannolikt kommer att orsaka prestandaproblem när de kompileras om med ett atypiskt parametervärde.

Vad är en parameter?

Vissa skulle säga att en SELECT sats som refererar till en lokal variabel är en "parameteriserad sats" typ, men det är inte den definition som SQL Server använder.

En rimlig indikation på att en sats använder parametrar kan hittas genom att titta på planens egenskaper (se Parametrar fliken i Sentry One Plan Explorer. Eller klicka på frågeplanens rotnod i SSMS, öppna Egenskaper fönstret och expandera Parameterlistan nod):

Det "kompilerade värdet" visar det sniffade värdet för parametern som används för att kompilera den cachade planen. "Rörtidsvärdet" visar värdet på parametern för den specifika exekveringen som fångas i planen.

Endera av dessa egenskaper kan vara tomma eller saknas under olika omständigheter. Om en fråga inte parametriseras kommer egenskaperna helt enkelt saknas.

Bara för att ingenting någonsin är enkelt i SQL Server, finns det situationer där parameterlistan kan fyllas i, men satsen är fortfarande inte parametriserad. Detta kan inträffa när SQL Server försöker enkel parameterisering (diskuteras senare) men beslutar att försöket är "osäkert". I så fall kommer parametermarkörer att finnas, men exekveringsplanen är inte parametriserad.

Sniffing är inte bara för lagrade procedurer

Parametersniffning sker också när en batch explicit parametriseras för återanvändning med sp_executesql .

Till exempel:

EXECUTE sys.sp_executesql
    N'
    SELECT
        P.ProductID,
        P.Name,
        TotalQty = SUM(TH.Quantity)
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID
    WHERE
        P.Name LIKE @NameLike
    GROUP BY
        P.ProductID,
        P.Name;
    ',
    N'@NameLike nvarchar(50)',
    @NameLike = N'K%';

Optimeraren väljer en exekveringsplan baserat på det sniffade värdet för @NameLike parameter. Parametervärdet "K%" uppskattas matcha väldigt få rader i Product tabell, så att optimeraren väljer en kapslad loop-join- och nyckeluppslagsstrategi:

Om du kör satsen igen med ett parametervärde på "[H-R]%" (som kommer att matcha många fler rader) återanvänds den cachade parametriserade planen:

EXECUTE sys.sp_executesql
    N'
    SELECT
        P.ProductID,
        P.Name,
        TotalQty = SUM(TH.Quantity)
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID
    WHERE
        P.Name LIKE @NameLike
    GROUP BY
        P.ProductID,
        P.Name;
    ',
    N'@NameLike nvarchar(50)',
    @NameLike = N'[H-R]%';

AdventureWorks exempeldatabasen är för liten för att göra detta till en prestandakatastrof, men den här planen är verkligen inte optimal för det andra parametervärdet.

Vi kan se planen som optimeraren skulle ha valt genom att rensa plancachen och köra den andra frågan igen:

Med ett större antal förväntade matchningar avgör optimeraren att en hash-join och hash-aggregering är bättre strategier.

T-SQL-funktioner

Parametersniffning förekommer också med T-SQL-funktioner, även om hur exekveringsplaner genereras kan göra det svårare att se.

Det finns goda skäl att undvika T-SQL-skalär- och flerpåståendefunktioner i allmänhet, så här är en T-SQL-funktionsversion med flera påståenden av vår testfråga endast för utbildningsändamål:

CREATE FUNCTION dbo.F
    (@NameLike nvarchar(50))
RETURNS @Result TABLE
(
    ProductID   integer NOT NULL PRIMARY KEY,
    Name        nvarchar(50) NOT NULL,
    TotalQty    integer NOT NULL
)
WITH SCHEMABINDING
AS
BEGIN
    INSERT @Result
    SELECT
        P.ProductID,
        P.Name,
        TotalQty = SUM(TH.Quantity)
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID
    WHERE
        P.Name LIKE @NameLike
    GROUP BY
        P.ProductID,
        P.Name;
 
    RETURN;
END;

Följande fråga använder funktionen för att visa information för produktnamn som börjar med 'K':

SELECT
    Result.ProductID,
    Result.Name,
    Result.TotalQty
FROM dbo.F(N'K%') AS Result;

Att se parametersniffning med en inbäddad funktion är svårare eftersom SQL Server inte returnerar en separat efterkörning (faktisk) frågeplan för varje funktionsanrop. Funktionen kan anropas många gånger inom ett enda uttalande, och användare skulle inte bli imponerade om SSMS försökte visa en miljon funktionsanropsplaner för en enda fråga.

Som ett resultat av detta designbeslut är den faktiska planen som returneras av SQL Server för vår testfråga inte särskilt användbar:

Ändå finns det sätt att se parametersniffning i aktion med inbäddade funktioner. Metoden jag har valt att använda här är att inspektera plancachen:

SELECT
    DEQS.plan_generation_num,
    DEQS.execution_count,
    DEQS.last_logical_reads,
    DEQS.last_elapsed_time,
    DEQS.last_rows,
    DEQP.query_plan
FROM sys.dm_exec_query_stats AS DEQS
CROSS APPLY sys.dm_exec_sql_text(DEQS.plan_handle) AS DEST
CROSS APPLY sys.dm_exec_query_plan(DEQS.plan_handle) AS DEQP
WHERE
    DEST.objectid = OBJECT_ID(N'dbo.F', N'TF');

Detta resultat visar att funktionsplanen har exekverats en gång, till en kostnad av 201 logiska läsningar med 2891 mikrosekunders förfluten tid, och den senaste exekveringen returnerade en rad. XML-planrepresentationen som returneras visar att parametervärdet var sniffade:

Kör nu satsen igen, med en annan parameter:

SELECT
    Result.ProductID,
    Result.Name,
    Result.TotalQty
FROM dbo.F(N'[H-R]%') AS Result;

Efterkörningsplanen visar att 306 rader returnerades av funktionen:

Plancachefrågan visar att den cachade exekveringsplanen för funktionen har återanvänts (execution_count =2):

Den visar också ett mycket högre antal logiska avläsningar och en längre förfluten tid jämfört med föregående körning. Detta är förenligt med återanvändning av kapslade loopar och uppslagsplan, men för att vara helt säker kan funktionsplanen efter körning fångas med Utökade händelser eller SQL Server Profiler verktyg:

Eftersom parametersniffning gäller funktioner kan dessa moduler drabbas av samma oväntade förändringar i prestanda som vanligtvis förknippas med lagrade procedurer.

Till exempel, första gången en funktion refereras, kan en plan cachelagras som inte använder parallellism. Efterföljande körningar med parametervärden som skulle gynnas av parallellitet (men återanvänder den cachade serieplanen) kommer att visa oväntat dålig prestanda.

Det här problemet kan vara svårt att identifiera eftersom SQL Server inte returnerar separata planer efter exekvering för funktionsanrop som vi har sett. Använda Utökade evenemang eller Profiler att rutinmässigt fånga planer efter utförande kan vara extremt resurskrävande, så det är ofta vettigt att använda den tekniken på ett mycket riktat sätt. Svårigheterna med att felsöka problem med funktionsparameterkänslighet gör att det är ännu mer värt att göra en analys (och koda defensivt) innan funktionen kommer i produktion.

Parametersniffning fungerar på exakt samma sätt med T-SQL skalära användardefinierade funktioner (såvida de inte är inbyggda, på SQL Server 2019 och framåt). In-line tabellvärdade funktioner genererar inte en separat exekveringsplan för varje anrop, eftersom (som namnet säger) dessa är infogade i anropsfrågan före kompilering.

Se upp Sniffed NULLs

Rensa planens cache och begär en uppskattad (förutförande) plan för testfrågan:

SELECT
    Result.ProductID,
    Result.Name,
    Result.TotalQty
FROM dbo.F(N'K%') AS Result;

Du kommer att se två exekveringsplaner, varav den andra är för funktionsanropet:

En begränsning av parametersniffning med inbäddade funktioner i uppskattade planer innebär att parametervärdet sniffas som NULL (inte "K%"):

I versioner av SQL Server före 2012, denna plan (optimerad för en NULL parameter) cachelagras för återanvändning . Detta är olyckligt eftersom NULL är osannolikt ett representativt parametervärde, och det var verkligen inte det värde som angavs i frågan.

SQL Server 2012 (och senare) cachelagrar inte planer som härrör från en "uppskattad plan"-begäran, även om den fortfarande visar en funktionsplan optimerad för en NULL parametervärde vid kompileringstillfället.

Enkel och påtvingad parametrering

En ad-hoc T-SQL-sats som innehåller konstanta bokstavliga värden kan parametriseras av SQL Server, antingen för att frågan kvalificerar sig för enkel parameterisering eller för att databasalternativet för forcerad parametrisering är aktiverat (eller en planguide används med samma effekt).

Ett påstående parametriserat på detta sätt är också föremål för parametersniffning. Följande fråga kvalificerar sig för enkel parametrering:

SELECT 
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE 
    A.AddressLine1 = N'Heidestieg Straße 8664';

Den uppskattade utförandeplanen visar en uppskattning av 2,5 rader baserat på det sniffade parametervärdet:

Faktum är att frågan returnerar 7 rader (kardinalitetsuppskattningen är inte perfekt, även när värden sniffas):

Vid det här laget undrar du kanske var bevisen är att den här frågan parametrerades och att det resulterande parametervärdet sniffades. Kör frågan en andra gång med ett annat värde:

SELECT 
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE 
    A.AddressLine1 = N'Winter der Böck 8550';

Frågan returnerar en rad:

Exekveringsplanen visar att den andra exekveringen återanvänds den parametriserade planen som kompilerades med ett sniffat värde:

Parameterisering och sniffning är separata aktiviteter

En ad-hoc-sats kan parametriseras av SQL Server utan parametervärden sniffas.

För att demonstrera kan vi använda spårningsflagga 4136 för att inaktivera parametersniffning för en batch som kommer att parametriseras av servern:

DBCC FREEPROCCACHE;
DBCC TRACEON (4136);
GO
SELECT
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE
    A.AddressLine1 = N'Heidestieg Straße 8664';
GO
SELECT 
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE 
    A.AddressLine1 = N'Winter der Böck 8550';
GO
DBCC TRACEOFF (4136);

Skriptet resulterar i satser som parametriseras, men parametervärdet sniffas inte för kardinalitetsuppskattning. För att se detta kan vi inspektera planens cache:

WITH XMLNAMESPACES
    (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT
    DECP.cacheobjtype,
    DECP.objtype,
    DECP.usecounts,
    DECP.plan_handle,
    parameterized_plan_handle =
        DEQP.query_plan.value
        (
            '(//StmtSimple)[1]/@ParameterizedPlanHandle',
            'NVARCHAR(100)'
        )
FROM sys.dm_exec_cached_plans AS DECP
CROSS APPLY sys.dm_exec_sql_text(DECP.plan_handle) AS DEST
CROSS APPLY sys.dm_exec_query_plan(DECP.plan_handle) AS DEQP
WHERE 
    DEST.[text] LIKE N'%AddressLine1%'
    AND DEST.[text] NOT LIKE N'%XMLNAMESPACES%';

Resultaten visar två cache-poster för ad-hoc-frågorna, länkade till den parametriserade (förberedda) frågeplanen av det parametriserade planhandtaget.

Den parametrerade planen används två gånger:

Utförandeplanen visar en annan kardinalitetsuppskattning nu när parametersniffning är inaktiverad:

Jämför uppskattningen av 1,44571 rader med uppskattningen på 2,5 rader som användes när parametersniffning var aktiverad.

Med sniffning inaktiverad kommer uppskattningen från genomsnittlig frekvensinformation om AddressLine1 kolumn. Ett utdrag av DBCC SHOW_STATISTICS utdata för indexet i fråga visar hur detta antal beräknades:Multiplicera antalet rader i tabellen (19 614) med densiteten (7,370826e-5) ger uppskattningen av 1,44571 rader.

Sidoanteckning: Det anses allmänt att endast heltalsjämförelser som använder ett unikt index kan kvalificera sig för enkel parameterisering. Jag valde medvetet det här exemplet (en strängjämförelse med ett icke-unikt index) för att motbevisa det.

Med RECOMPILE och OPTION (RECOMPILE)

När ett parameterkänslighetsproblem stöter på är ett vanligt råd på forum och fråge- och svarsplatser att "använda omkompilera" (förutsatt att de andra inställningsalternativen som presenterades tidigare är olämpliga). Tyvärr misstolkas det rådet ofta som att man lägger till WITH RECOMPILE alternativet till den lagrade proceduren.

Använder WITH RECOMPILE återför oss effektivt till SQL Server 2000-beteende, där hela lagrade proceduren kompileras om vid varje körning.

Ett bättre alternativ , på SQL Server 2005 och senare, är att använda OPTION (RECOMPILE) frågetips på bara påståendet som lider av parametersniffningsproblemet. Denna frågetips resulterar i en omkompilering av den problematiska satsen endast. Exekveringsplaner för andra uttalanden inom den lagrade proceduren cachelagras och återanvänds som vanligt.

Använder WITH RECOMPILE betyder också att den kompilerade planen för den lagrade proceduren inte är cachad. Som ett resultat av detta underhålls ingen prestandainformation i DMV:er som sys.dm_exec_query_stats .

Att använda frågetipset betyder istället att en kompilerad plan kan cachelagras och prestandainformation är tillgänglig i DMV:erna (även om den är begränsad till den senaste körningen, endast för den berörda satsen).

För instanser som kör minst SQL Server 2008 build 2746 (Service Pack 1 med kumulativ uppdatering 5), med OPTION (RECOMPILE) har en annan betydande fördel över WITH RECOMPILE :Endast OPTION (RECOMPILE) aktiverar Optimering av parameterinbäddning .

Optimering av parameterinbäddning

Genom att sniffa parametervärden kan optimeraren använda parametervärdet för att härleda kardinalitetsuppskattningar. Båda WITH RECOMPILE och OPTION (RECOMPILE) resultera i frågeplaner med uppskattningar som beräknas utifrån de faktiska parametervärdena vid varje exekvering.

Optimering av parameterinbäddning tar denna process ett steg längre. Frågeparametrar ersätts med bokstavliga konstantvärden under frågeanalys.

Parsern kan göra förvånansvärt komplexa förenklingar, och efterföljande frågeoptimering kan förfina saker ytterligare. Tänk på följande lagrade procedur, som innehåller WITH RECOMPILE alternativ:

CREATE PROCEDURE dbo.P
    @NameLike nvarchar(50),
    @Sort tinyint
WITH RECOMPILE
AS
BEGIN
    SELECT TOP (5)
        ProductID,
        Name
    FROM Production.Product
    WHERE
        @NameLike IS NULL
        OR Name LIKE @NameLike
    ORDER BY
        CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC,
        CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC,
        CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC,
        CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC;
END;

Proceduren utförs två gånger, med följande parametervärden:

EXECUTE dbo.P
	@NameLike = N'K%',
	@Sort = 1;
GO
EXECUTE dbo.P
	@NameLike = N'[H-R]%',
	@Sort = 4;

Eftersom WITH RECOMPILE används, kompileras proceduren helt om vid varje körning. Parametervärdena är sniffade varje gång och används av optimeraren för att beräkna kardinalitetsuppskattningar.

Planen för den första procedurexekveringen är exakt korrekt, uppskattningsvis 1 rad:

Den andra körningen uppskattar 360 rader, mycket nära de 366 som sågs vid körning:

Båda planerna använder samma allmänna exekveringsstrategi:Skanna alla rader i ett index med WHERE satspredikat som en residual; beräkna CASE uttryck som används i ORDER BY klausul; och utför en Topp N Sortering på resultatet av CASE uttryck.

ALTERNATIV (OMKOMPILERA)

Återskapa nu den lagrade proceduren med ett OPTION (RECOMPILE) frågetips istället för WITH RECOMPILE :

CREATE PROCEDURE dbo.P
    @NameLike nvarchar(50),
    @Sort tinyint
AS
BEGIN
    SELECT TOP (5)
        ProductID,
        Name
    FROM Production.Product
    WHERE
        @NameLike IS NULL
        OR Name LIKE @NameLike
    ORDER BY
        CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC,
        CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC,
        CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC,
        CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC
    OPTION (RECOMPILE);
END;

Att köra den lagrade proceduren två gånger med samma parametervärden som tidigare ger dramatiskt olika genomförandeplaner.

Detta är den första exekveringsplanen (med parametrar som begär namn som börjar med "K", sorterade efter ProductID stigande):

Parsern bäddar in parametervärdena i frågetexten, vilket resulterar i följande mellanform:

SELECT TOP (5)
    ProductID,
    Name
FROM Production.Product
WHERE
    'K%' IS NULL
    OR Name LIKE 'K%'
ORDER BY
    CASE WHEN 1 = 1 THEN ProductID ELSE NULL END ASC,
    CASE WHEN 1 = 2 THEN ProductID ELSE NULL END DESC,
    CASE WHEN 1 = 3 THEN Name ELSE NULL END ASC,
    CASE WHEN 1 = 4 THEN Name ELSE NULL END DESC;

Parsern går sedan längre, tar bort motsägelser och utvärderar CASE fullständigt uttryck. Detta resulterar i:

SELECT TOP (5)
    ProductID,
    Name
FROM Production.Product
WHERE
    Name LIKE 'K%'
ORDER BY
    ProductID ASC,
    NULL DESC,
    NULL ASC,
    NULL DESC;

Du kommer att få ett felmeddelande om du försöker skicka den frågan direkt till SQL Server, eftersom beställning efter ett konstant värde inte är tillåtet. Ändå är detta den form som produceras av analysatorn. Det är tillåtet internt eftersom det uppstod som ett resultat av tillämpningen av parameterinbäddningsoptimering . Den förenklade frågan gör livet mycket lättare för frågeoptimeraren:

Clustered Index Scan tillämpar LIKE predikat som en rest. Compute Scalar tillhandahåller konstanten NULL värden. Topp returnerar de första 5 raderna i den ordning som tillhandahålls av Clustered Index (undviker en sorts). I en perfekt värld skulle frågeoptimeraren också ta bort Compute Scalar som definierar NULLs , eftersom de inte används under körning av en fråga.

Den andra exekveringen följer exakt samma process, vilket resulterar i en frågeplan (för namn som börjar med bokstäverna "H" till "R", sorterade efter Name fallande) så här:

Den här planen innehåller en Icke-klusterad indexsökning som täcker LIKE intervall, en återstående LIKE predikat, konstanten NULLs som tidigare, och en topp (5). Frågeoptimeraren väljer att utföra en BACKWARD intervallsökning i Indexsökning för att återigen undvika sortering.

Jämför planen ovan med den som skapats med WITH RECOMPILE , som inte kan använda parameterinbäddningsoptimering :

Det här demoexemplet kan ha implementerats bättre som en serie IF satser i proceduren (en för varje kombination av parametervärden). Detta kan ge liknande fördelar med frågeplaner, utan att det krävs en sammanställning av ett uttalande varje gång. I mer komplexa scenarier kompilerar påståendenivån om med parameterinbäddning som tillhandahålls av OPTION (RECOMPILE) kan vara en extremt användbar optimeringsteknik.

En inbäddningsbegränsning

Det finns ett scenario där OPTION (RECOMPILE) används kommer inte att resultera i att parameterinbäddningsoptimering tillämpas. Om satsen tilldelar en variabel, bäddas inte parametervärden in:

CREATE PROCEDURE dbo.P
    @NameLike nvarchar(50),
    @Sort tinyint
AS
BEGIN
    DECLARE
        @ProductID integer,
        @Name nvarchar(50);
 
    SELECT TOP (1)
        @ProductID = ProductID,
        @Name = Name
    FROM Production.Product
    WHERE
        @NameLike IS NULL
        OR Name LIKE @NameLike
    ORDER BY
        CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC,
        CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC,
        CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC,
        CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC
    OPTION (RECOMPILE);
END;

Eftersom SELECT sats nu tilldelar till en variabel, frågeplanerna som skapas är desamma som när WITH RECOMPILE var använd. Parametervärden sniffas fortfarande och används av frågeoptimeraren för uppskattning av kardinalitet och OPTION (RECOMPILE) kompilerar fortfarande bara den enda satsen, bara fördelen med parameterinbäddning är förlorad.


  1. Hur man ändrar användare till superanvändare i PostgreSQL

  2. Skillnaden mellan TRIM() och TRIM_ORACLE() i MariaDB

  3. Hämtar kompletta felmeddelanden i isql

  4. 2 sätt att sammanfoga strängar och siffror i MariaDB