Parametersniffing
Forespørgselsparameterisering fremmer genbrugen af cachelagrede eksekveringsplaner og undgår derved unødvendige kompileringer og reducerer antallet af ad-hoc-forespørgsler i plancachen.
Disse er alle gode ting, forudsat forespørgslen, der parametreres, burde virkelig bruge den samme cachelagrede udførelsesplan for forskellige parameterværdier. En eksekveringsplan, der er effektiv for én parameterværdi, ikke være et godt valg for andre mulige parameterværdier.
Når parametersniffing er aktiveret (standard), vælger SQL Server en eksekveringsplan baseret på de særlige parameterværdier, der eksisterer på kompileringstidspunktet. Den implicitte antagelse er, at parametriserede udsagn oftest udføres med de mest almindelige parameterværdier. Dette lyder rimeligt nok (endog indlysende), og det fungerer faktisk ofte godt.
Der kan opstå et problem, når en automatisk genkompilering af den cachelagrede plan opstår. En genkompilering kan udløses af alle mulige årsager, for eksempel fordi et indeks brugt af den cachelagrede plan er blevet slettet (en korrekthed genkompilering), eller fordi statistiske oplysninger er ændret (en optimalitet genkompilere).
Uanset den nøjagtige årsag af planens genkompilering er der en chance for, at en atypisk værdi overføres som en parameter på det tidspunkt, hvor den nye plan genereres. Dette kan resultere i en ny cachelagret plan (baseret på den sniffede atypiske parameterværdi), som ikke er god til de fleste udførelser, som den vil blive genbrugt til.
Det er ikke let at forudsige, hvornår en bestemt eksekveringsplan vil blive rekompileret (for eksempel fordi statistikken har ændret sig tilstrækkeligt), hvilket resulterer i en situation, hvor en genanvendelig plan af god kvalitet pludselig kan erstattes af en helt anden plan optimeret til atypiske parameterværdier.
Et sådant scenarie opstår, når den atypiske værdi er meget selektiv, hvilket resulterer i en plan, der er optimeret til et lille antal rækker. Sådanne planer vil ofte bruge enkelttrådsudførelse, indlejrede loops-sammenføjninger og opslag. Der kan opstå alvorlige ydeevneproblemer, når denne plan genbruges til forskellige parameterværdier, der genererer et meget større antal rækker.
Deaktivering af parametersniffing
Parametersniffing kan deaktiveres ved hjælp af dokumenteret sporingsflag 4136. Sporingsflaget understøttes også for per-forespørgsel brug via QUERYTRACEON
forespørgselstip. Begge gælder fra SQL Server 2005 Service Pack 4 og frem (og lidt tidligere, hvis du anvender kumulative opdateringer til Service Pack 3).
Fra og med SQL Server 2016 kan parametersniffing også deaktiveres på databaseniveau , ved hjælp af PARAMETER_SNIFFING
argument til ALTER DATABASE SCOPED CONFIGURATION
.
Når parametersniffing er deaktiveret, bruger SQL Server gennemsnitlig distribution statistik for at vælge en eksekveringsplan.
Dette lyder også som en fornuftig tilgang (og kan hjælpe med at undgå situationen, hvor planen er optimeret til en usædvanlig selektiv parameterværdi), men det er heller ikke en perfekt strategi:En plan optimeret til en 'gennemsnitlig' værdi kan meget vel ende med at blive seriøst suboptimal for de almindeligt sete parameterværdier.
Overvej en eksekveringsplan, der indeholder hukommelseskrævende operatører som sorteringer og hashes. Fordi hukommelse er reserveret før udførelse af forespørgsler starter, kan en parametriseret plan baseret på gennemsnitlige distributionsværdier spildes til tempdb for almindelige parameterværdier, der producerer flere data, end optimeringsværktøjet forventede.
Hukommelsesreservationer kan normalt ikke vokse under udførelse af forespørgsler, uanset hvor meget ledig hukommelse serveren måtte have. Visse applikationer har godt af at slå parametersniffing fra (se dette arkivindlæg af Dynamics AX Performance Team for et eksempel).
For de fleste arbejdsbelastninger er fuldstændig deaktivering af parametersniffing den forkerte løsning , og kan endda være en katastrofe. Parametersniffing er en heuristisk optimering:Det virker bedre end at bruge gennemsnitsværdier på de fleste systemer, det meste af tiden.
Forespørgselstip
SQL Server giver en række forespørgselstip og andre muligheder for at justere adfærden ved parametersniffing:
OPTIMER FOR (@parameter =værdi)
forespørgselstip bygger en genanvendelig plan baseret på en specifik værdi.OPTIMER TIL (@parameter UKENDT)
bruger gennemsnitlig distributionsstatistik for en bestemt parameter.OPTIMER TIL UKENDT
bruger gennemsnitlig fordeling for alle parametre (samme effekt som sporingsflag 4136).-
MED REKOMPILERING optionen lagret procedure kompilerer en ny procedureplan for hver udførelse. MULIGHED (GENKOMPILER) forespørgselstip kompilerer en ny plan for en individuel erklæring.
Den gamle teknik med "parameterskjul" (at tildele procedureparametre til lokale variabler og i stedet henvise til variablerne) har samme effekt som at specificere OPTIMER TIL UKENDT
. Det kan være nyttigt på instanser tidligere end SQL Server 2008 (OPTIMIZE FOR
tip var nyt for 2008).
Det kunne hævdes, at hver parameteriseret sætning skal kontrolleres for følsomhed over for parameterværdier og enten lades alene (hvis standardadfærden fungerer godt) eller eksplicit antydes ved hjælp af en af mulighederne ovenfor.
Dette gøres sjældent i praksis, blandt andet fordi at udføre en omfattende analyse for alle mulige parameterværdier kan være tidskrævende og kræver ret avancerede færdigheder.
Oftest udføres der ikke en sådan analyse, og parameterfølsomhedsproblemer behandles som og når de opstår i produktionen.
Denne mangel på forudgående analyse er sandsynligvis en hovedårsag til, at parametersniffing har et dårligt ry. Det betaler sig at være opmærksom på potentialet for, at problemer kan opstå, og at udføre i det mindste en hurtig analyse af udsagn, der sandsynligvis vil forårsage præstationsproblemer, når de genkompileres med en atypisk parameterværdi.
Hvad er en parameter?
Nogle vil sige, at en SELECT
sætning, der refererer til en lokal variabel, er en "parameteriseret sætning" af slagsen, men det er ikke den definition, SQL Server bruger.
En rimelig indikation af, at en sætning bruger parametre, kan findes ved at se på planegenskaberne (se parametrene fanen i Sentry One Plan Explorer. Eller klik på forespørgselsplanens rodknude i SSMS, åbn Egenskaber vinduet, og udvid Parameterlisten node):
Den 'kompilerede værdi' viser den sniffede værdi af den parameter, der bruges til at kompilere den cachelagrede plan. "Kørselsværdien" viser værdien af parameteren på den bestemte udførelse, der er fanget i planen.
En af disse egenskaber kan være tomme eller mangle under forskellige omstændigheder. Hvis en forespørgsel ikke er parametriseret, vil egenskaberne simpelthen alle mangle.
Bare fordi intet nogensinde er enkelt i SQL Server, er der situationer, hvor parameterlisten kan udfyldes, men sætningen er stadig ikke parametriseret. Dette kan forekomme, når SQL Server forsøger simpel parameterisering (diskuteret senere), men beslutter, at forsøget er "usikkert". I så fald vil parametermarkører være til stede, men udførelsesplanen er faktisk ikke parametriseret.
Sniffing er ikke kun for lagrede procedurer
Parametersniffing forekommer også, når en batch eksplicit er parametriseret til genbrug ved hjælp af sp_executesql
.
For eksempel:
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%';
Optimizeren vælger en eksekveringsplan baseret på den sniffede værdi af @NameLike
parameter. Parameterværdien "K%" estimeres til at matche meget få rækker i Produkt
tabel, så optimeringsværktøjet vælger en indlejret loop join- og nøgleopslagsstrategi:
Udførelse af sætningen igen med en parameterværdi på "[H-R]%" (som vil matche mange flere rækker) genbruger den cachelagrede parametriserede plan:
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 prøvedatabasen er for lille til at gøre dette til en ydeevnekatastrofe, men denne plan er bestemt ikke optimal for den anden parameterværdi.
Vi kan se den plan, optimizeren ville have valgt ved at rydde planens cache og udføre den anden forespørgsel igen:
Med et større antal matches forventet, bestemmer optimeringsværktøjet, at en hash-join og hash-aggregering er bedre strategier.
T-SQL-funktioner
Parametersniffing forekommer også med T-SQL-funktioner, selvom den måde, hvorpå eksekveringsplaner genereres, kan gøre dette sværere at se.
Der er gode grunde til at undgå T-SQL skalar- og multi-sætningsfunktioner generelt, så kun til undervisningsformål er her en T-SQL multi-sætning tabel-værdi-funktionsversion af vores testforespørgsel:
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ølgende forespørgsel bruger funktionen til at vise information om produktnavne, der starter med 'K':
SELECT Result.ProductID, Result.Name, Result.TotalQty FROM dbo.F(N'K%') AS Result;
Det er vanskeligere at se parametersniffing med en indlejret funktion, fordi SQL Server ikke returnerer en separat post-execution (faktisk) forespørgselsplan for hver funktionsankaldelse. Funktionen kunne kaldes mange gange inden for en enkelt erklæring, og brugere ville ikke blive imponerede, hvis SSMS forsøgte at vise en million funktionsopkaldsplaner for en enkelt forespørgsel.
Som et resultat af denne designbeslutning er den faktiske plan, der returneres af SQL Server til vores testforespørgsel, ikke særlig nyttig:
Ikke desto mindre er der er måder at se parametersniffing i aktion med indlejrede funktioner. Metoden jeg har valgt at bruge her er at inspicere planens cache:
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');
Dette resultat viser, at funktionsplanen er blevet udført én gang, til en pris af 201 logiske læsninger med 2891 mikrosekunders forløbet tid, og den seneste udførelse returnerede én række. Den returnerede XML-planrepræsentation viser, at parameterværdien var snusede:
Kør nu sætningen igen med en anden parameter:
SELECT Result.ProductID, Result.Name, Result.TotalQty FROM dbo.F(N'[H-R]%') AS Result;
Efterudførelsesplanen viser, at 306 rækker blev returneret af funktionen:
Plancache-forespørgslen viser, at den cachelagrede eksekveringsplan for funktionen er blevet genbrugt (execution_count
=2):
Den viser også et meget højere antal logiske aflæsninger og en længere forløbet tid sammenlignet med den forrige kørsel. Dette er i overensstemmelse med genbrug af indlejrede sløjfer og opslagsplan, men for at være helt sikker kan funktionsplanen efter udførelse fanges ved hjælp af Udvidede hændelser eller SQL Server Profiler værktøj:
Fordi parametersniffing gælder for funktioner, kan disse moduler lide under de samme uventede ændringer i ydeevnen, som normalt er forbundet med lagrede procedurer.
For eksempel, første gang der refereres til en funktion, kan en plan cachelagres, der ikke bruger parallelitet. Efterfølgende eksekveringer med parameterværdier, der ville drage fordel af parallelitet (men genbrug den cachelagrede serielle plan), vil vise uventet dårlig ydeevne.
Dette problem kan være vanskeligt at identificere, fordi SQL Server ikke returnerer separate efterudførelsesplaner for funktionskald, som vi har set. Brug af Udvidede begivenheder eller Profiler rutinemæssigt at fange efterudførelsesplaner kan være ekstremt ressourcekrævende, så det giver ofte mening at bruge den teknik på en meget målrettet måde. Vanskelighederne omkring fejlfinding af funktionsparametre-følsomhed betyder, at det er endnu mere værd at lave en analyse (og kode defensivt), før funktionen rammer produktionen.
Parameter-sniffing fungerer på nøjagtig samme måde med T-SQL skalære brugerdefinerede funktioner (medmindre de er in-linet, på SQL Server 2019 og frem). Funktioner med tabelværdi på linje genererer ikke en separat eksekveringsplan for hver påkaldelse, fordi (som navnet siger) disse er inlinet i den kaldende forespørgsel før kompilering.
Pas på Sniffed NULLs
Ryd planens cache, og anmod om en estimeret (præ-udførelse) plan for testforespørgslen:
SELECT Result.ProductID, Result.Name, Result.TotalQty FROM dbo.F(N'K%') AS Result;
Du vil se to eksekveringsplaner, hvoraf den anden er for funktionskaldet:
En begrænsning af parametersniffing med indlejrede funktioner i estimerede planer betyder, at parameterværdien sniffes som NULL
(ikke "K%"):
I versioner af SQL Server før 2012 er denne plan (optimeret til en NULL
parameter) er cachelagret til genbrug . Dette er uheldigt, fordi NULL
er usandsynligt, at det er en repræsentativ parameterværdi, og det var bestemt ikke den værdi, der er angivet i forespørgslen.
SQL Server 2012 (og senere) cacherer ikke planer, der er et resultat af en "estimeret plan"-anmodning, selvom den stadig vil vise en funktionsplan optimeret til en NULL
parameterværdi på kompileringstidspunktet.
Simpel og tvungen parametrering
En ad-hoc T-SQL-sætning, der indeholder konstante bogstavelige værdier, kan parametreres af SQL Server, enten fordi forespørgslen kvalificerer sig til simpel parametrering, eller fordi databaseindstillingen for tvungen parametrering er aktiveret (eller en planguide bruges til samme effekt).
Et udsagn, der er parameteriseret på denne måde, er også genstand for parametersniffing. Følgende forespørgsel kvalificerer til simpel parametrering:
SELECT A.AddressLine1, A.City, A.PostalCode FROM Person.Address AS A WHERE A.AddressLine1 = N'Heidestieg Straße 8664';
Den estimerede udførelsesplan viser et estimat på 2,5 rækker baseret på den sniffede parameterværdi:
Faktisk returnerer forespørgslen 7 rækker (kardinalitetsestimat er ikke perfekt, selv hvor værdier sniffes):
På dette tidspunkt undrer du dig måske over, hvor beviset er for, at denne forespørgsel blev parameteriseret, og den resulterende parameterværdi snusede. Kør forespørgslen en anden gang med en anden værdi:
SELECT A.AddressLine1, A.City, A.PostalCode FROM Person.Address AS A WHERE A.AddressLine1 = N'Winter der Böck 8550';
Forespørgslen returnerer én række:
Udførelsesplanen viser den anden udførelse genbrugte den parametriserede plan, der blev kompileret ved hjælp af en sniffed værdi:
Parameterisering og sniffning er separate aktiviteter
En ad-hoc-sætning kan parametreres af SQL Server uden parameterværdier, der sniffes.
For at demonstrere kan vi bruge sporingsflag 4136 til at deaktivere parametersniffing for en batch, der vil blive parameteriseret af serveren:
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);
Scriptet resulterer i udsagn, der er parametriseret, men parameterværdien sniffes ikke med henblik på kardinalitetsestimering. For at se dette kan vi inspicere 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%';
Resultaterne viser to cache-indgange for ad-hoc-forespørgslerne, knyttet til den parametriserede (forberedte) forespørgselsplan af det parameteriserede planhåndtag.
Den parametrerede plan bruges to gange:
Udførelsesplanen viser et andet kardinalitetsestimat nu, hvor parametersniffing er deaktiveret:
Sammenlign estimatet på 1,44571 rækker med estimatet på 2,5 rækker, der blev brugt, da parametersniffing var aktiveret.
Med sniffing deaktiveret kommer estimatet fra oplysninger om gennemsnitlig frekvens om AddressLine1
kolonne. Et uddrag af DBCC SHOW_STATISTICS
output for det pågældende indeks viser, hvordan dette tal blev beregnet:Multiplicer antallet af rækker i tabellen (19.614) med tætheden (7.370826e-5) giver det 1.44571 rækkeestimat.
Sidebemærkning: Det antages almindeligvis, at kun heltalssammenligninger ved hjælp af et unikt indeks kan kvalificere sig til simpel parameterisering. Jeg valgte bevidst dette eksempel (en strengsammenligning ved hjælp af et ikke-unik indeks) for at modbevise det.
Med RECOMPILE og OPTION (RECOMPILE)
Når der stødes på et parameterfølsomhedsproblem, er et almindeligt råd på fora og Q&A-websteder at "bruge rekompilere" (forudsat at de andre indstillingsmuligheder præsenteret tidligere er uegnede). Desværre bliver det råd ofte misfortolket til at betyde tilføjelse af WITH RECOMPILE
mulighed for den lagrede procedure.
Brug af WITH RECOMPILE
returnerer os effektivt til SQL Server 2000-adfærd, hvor hele lagrede procedure genkompileres ved hver udførelse.
Et bedre alternativ , på SQL Server 2005 og nyere, skal bruge OPTION (RECOMPILE)
forespørgselstip på kun erklæringen der lider af parameter-sniffing-problemet. Dette forespørgselstip resulterer i en genkompilering af den problematiske sætning kun. Udførelsesplaner for andre udsagn i den lagrede procedure cachelagres og genbruges som normalt.
Brug af WITH RECOMPILE
betyder også, at den kompilerede plan for den lagrede procedure ikke er cachelagret. Som følge heraf vedligeholdes ingen ydeevneoplysninger i DMV'er såsom sys.dm_exec_query_stats
.
Brug af forespørgselstip betyder i stedet, at en kompileret plan kan cachelagres, og ydeevneinformation er tilgængelig i DMV'erne (selvom den er begrænset til den seneste udførelse, kun for den berørte sætning).
For forekomster, der kører mindst SQL Server 2008 build 2746 (Service Pack 1 med kumulativ opdatering 5), ved hjælp af OPTION (RECOMPILE)
har en anden betydelig fordel over Med RECOMPILE
:Kun OPTION (GENKOMPILER)
aktiverer optimering af parameterindlejring .
Optimering af parameterindlejring
Sniffing af parameterværdier gør det muligt for optimeringsværktøjet at bruge parameterværdien til at udlede kardinalitetsestimater. Begge WITH RECOMPILE
og MULIGHED (GENKOMPILER)
resultere i forespørgselsplaner med estimater beregnet ud fra de faktiske parameterværdier ved hver udførelse.
Optimering af parameterindlejring tager denne proces et skridt videre. Forespørgselsparametre erstattes med bogstavelige konstantværdier under forespørgselsparsing.
Parseren er i stand til overraskende komplekse forenklinger, og efterfølgende forespørgselsoptimering kan forfine tingene yderligere. Overvej følgende lagrede procedure, som indeholder WITH RECOMPILE
mulighed:
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 udføres to gange med følgende parameterværdier:
EXECUTE dbo.P @NameLike = N'K%', @Sort = 1; GO EXECUTE dbo.P @NameLike = N'[H-R]%', @Sort = 4;
Fordi Med RECOMPILE
bruges, bliver proceduren fuldstændigt rekompileret ved hver udførelse. Parameterværdierne er sniffet hver gang og bruges af optimeringsværktøjet til at beregne kardinalitetsestimater.
Planen for den første procedureudførelse er nøjagtigt korrekt, idet der anslås 1 række:
Den anden udførelse anslår 360 rækker, meget tæt på de 366, der blev set under kørsel:
Begge planer bruger den samme generelle udførelsesstrategi:Scan alle rækker i et indeks ved at anvende WHERE
klausul prædikat som en residual; udregn CASE
udtryk brugt i ORDER BY
klausul; og udføre en Top N Sort på resultatet af CASE
udtryk.
MULIGHED (GENKOMPIL)
Genopret nu den lagrede procedure ved hjælp af en OPTION (RECOMPILE)
forespørgselstip i stedet for 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;
Udførelse af den lagrede procedure to gange med de samme parameterværdier som før giver dramatisk forskellige udførelsesplaner.
Dette er den første eksekveringsplan (med parametre, der anmoder om navne, der starter med "K", sorteret efter ProductID
stigende):
Parseren indlejrer parameterværdierne i forespørgselsteksten, hvilket resulterer i følgende mellemform:
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;
Parseren går derefter videre, fjerner modsigelser og evaluerer CASE
fuldstændigt udtryk. Dette resulterer i:
SELECT TOP (5) ProductID, Name FROM Production.Product WHERE Name LIKE 'K%' ORDER BY ProductID ASC, NULL DESC, NULL ASC, NULL DESC;
Du vil få en fejlmeddelelse, hvis du forsøger at sende den forespørgsel direkte til SQL Server, fordi bestilling efter en konstant værdi ikke er tilladt. Ikke desto mindre er dette den form, der produceres af parseren. Det er tilladt internt, fordi det er opstået som et resultat af anvendelse af parameterindlejringsoptimering . Den forenklede forespørgsel gør livet meget lettere for forespørgselsoptimeringsværktøjet:
Clustered Index Scan anvender LIKE
prædikat som et residual. Compute Scalar giver konstanten NULL
værdier. Toppen returnerer de første 5 rækker i rækkefølgen givet af Clustered Index (undgå en slags). I en perfekt verden ville forespørgselsoptimeringsværktøjet også fjerne Compute Scalar der definerer NULL'erne
, da de ikke bruges under udførelse af forespørgsler.
Den anden udførelse følger nøjagtig den samme proces, hvilket resulterer i en forespørgselsplan (for navne, der begynder med bogstaverne "H" til "R", sorteret efter Navn
faldende) sådan her:
Denne plan indeholder en Ikke-klynget indekssøgning der dækker LIKE
interval, en resterende LIKE
prædikat, konstanten NULLs
som før, og en Top (5). Forespørgselsoptimeringsværktøjet vælger at udføre en BACKWARD
rækkeviddescanning i indekssøgning for endnu en gang at undgå sortering.
Sammenlign planen ovenfor med den, der er fremstillet ved hjælp af WITH RECOMPILE
, som ikke kan bruge optimering af parameterindlejring :
Dette demo-eksempel kunne have været bedre implementeret som en serie af IF
udsagn i proceduren (en for hver kombination af parameterværdier). Dette kunne give lignende fordele ved forespørgselsplanen uden at pådrage sig en erklæringskompilering hver gang. I mere komplekse scenarier rekompileres sætningsniveauet med parameterindlejring leveret af OPTION (RECOMPILE)
kan være en yderst nyttig optimeringsteknik.
En indlejringsbegrænsning
Der er et scenarie, hvor man bruger OPTION (RECOMPILE)
vil ikke resultere i, at parameterindlejringsoptimeringen anvendes. Hvis sætningen tildeles til en variabel, er parameterværdier ikke indlejret:
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;
Fordi SELECT
sætning nu tildeles til en variabel, de producerede forespørgselsplaner er de samme som når WITH RECOMPILE
var brugt. Parameterværdier sniffes stadig og bruges af forespørgselsoptimeringsværktøjet til estimering af kardinalitet og OPTION (RECOMPILE)
kompilerer stadig kun den enkelte sætning, kun fordelen ved parameterindlejring er tabt.