Jeg vil ikke forsøge at forklare de fulde detaljer om parametersniffing, men kort sagt nej det gør det ikke altid hjælpe (og det kan hindre).
Forestil dig en tabel (T) med en primær nøgle og en indekseret datokolonne (A), i tabellen er der 1.000 rækker, 400 har samme værdi af A (lad os sige i dag 20130122), de resterende 600 rækker er de næste 600 dage , så kun 1 post pr. dato.
Denne forespørgsel:
SELECT *
FROM T
WHERE A = '20130122';
Vil give en anden eksekveringsplan til:
SELECT *
FROM T
WHERE A = '20130123';
Da statistikken vil indikere, at de første 400 ud af 1.000 rækker vil blive returneret, bør optimeringsværktøjet erkende, at en tabelscanning vil være mere effektiv end et bogmærkeopslag, hvorimod den anden kun vil give 1 rækker, så et bogmærkeopslag vil være meget mere effektiv.
Tilbage til dit spørgsmål, hvis vi gjorde dette til en procedure:
CREATE PROCEDURE dbo.GetFromT @Param DATE
AS
SELECT *
FROM T
WHERE A = @Param
Kør derefter
EXECUTE dbo.GetFromT '20130122'; --400 rows
Forespørgselsplanen med tabelscanningen vil blive brugt, hvis første gang du kører den, bruger du '20130123' som parameter, gemmer den bogmærkeopslagsplanen. Indtil de tidspunkter, hvor proceduren er genkompileret, vil planen forblive den samme. Gør noget som dette:
CREATE PROCEDURE dbo.GetFromT @Param VARCHAR(5)
AS
DECLARE @Param2 VARCHAR(5) = @Param;
SELECT *
FROM T
WHERE A = @Param2
Så køres dette:
EXECUTE dbo.GetFromT '20130122';
Selvom proceduren kompileres på én gang, flyder den ikke korrekt, så forespørgselsplanen, der blev oprettet ved den første kompilering, har ingen idé om, at @Param2 bliver det samme som @param, så optimeringsværktøjet (uden viden om, hvor mange rækker der skal forventer) antager, at 300 vil blive returneret (30%), da en tabelscanning vil være mere effektiv end et bogmærkeopslag. Hvis du kørte den samme procedure med '20130123' som parameter, ville det give den samme plan (uanset hvilken parameter den først blev aktiveret med), fordi statistikken ikke kan bruges til en ukendt værdi. Så det ville være mere effektivt at køre denne procedure for '20130122', men for alle andre værdier ville det være mindre effektivt end uden lokale parametre (forudsat at proceduren uden lokale parametre først blev påberåbt med alt andet end '20130122')
Nogle forespørgsler, der skal demonstreres, så du selv kan se udførelsesplaner
Opret skema og eksempeldata
CREATE TABLE T (ID INT IDENTITY(1, 1) PRIMARY KEY, A DATE NOT NULL, B INT,C INT, D INT, E INT);
CREATE NONCLUSTERED INDEX IX_T ON T (A);
INSERT T (A, B, C, D, E)
SELECT TOP 400 CAST('20130122' AS DATE), number, 2, 3, 4
FROM Master..spt_values
WHERE type = 'P'
UNION ALL
SELECT TOP 600 DATEADD(DAY, number, CAST('20130122' AS DATE)), number, 2, 3, 4
FROM Master..spt_values
WHERE Type = 'P';
GO
CREATE PROCEDURE dbo.GetFromT @Param DATE
AS
SELECT *
FROM T
WHERE A = @Param
GO
CREATE PROCEDURE dbo.GetFromT2 @Param DATE
AS
DECLARE @Param2 DATE = @Param;
SELECT *
FROM T
WHERE A = @Param2
GO
Kør procedurer (viser faktisk udførelsesplan):
EXECUTE GetFromT '20130122';
EXECUTE GetFromT '20130123';
EXECUTE GetFromT2 '20130122';
EXECUTE GetFromT2 '20130123';
GO
EXECUTE SP_RECOMPILE GetFromT;
EXECUTE SP_RECOMPILE GetFromT2;
GO
EXECUTE GetFromT '20130123';
EXECUTE GetFromT '20130122';
EXECUTE GetFromT2 '20130123';
EXECUTE GetFromT2 '20130122';
Du vil se det første gang GetFromT
er kompileret bruger den en tabelscanning og beholder denne, når den køres med parameteren '20130122', GetFromT2
bruger også en tabelscanning og beholder planen for '20130122'.
Efter at procedurerne er blevet indstillet til rekompilering og kørt igen (bemærk i en anden rækkefølge), GetFromT
bruger en bogmærke-loopup og beholder planen for '20130122', på trods af at man tidligere har vurderet, at en tabelscanning er en mere passende plan. GetFromT2
er upåvirket af ordren og har samme plan som før genoverholdelsen.
Så sammenfattende afhænger det af fordelingen af dine data og dine indekser, din frekvens af rekompilering og en smule held med hensyn til, om en procedure vil have gavn af at bruge lokale variabler. Det gør det bestemt ikke altid hjælp.
Forhåbentlig har jeg kastet lidt lys over effekten af at bruge lokale parametre, eksekveringsplaner og stored procedure compliation. Hvis jeg har fejlet fuldstændigt eller misset et nøglepunkt, kan en meget mere dybdegående forklaring findes her:
http://www.sommarskog.se/query-plan-mysteries.html