Jag kommer inte att försöka förklara alla detaljer om parametersniffning, men kort sagt, nej det gör det inte alltid hjälp (och det kan hindra).
Föreställ dig en tabell (T) med en primärnyckel och en indexerad datumkolumn (A), i tabellen finns det 1 000 rader, 400 har samma värde på A (låt säga idag 20130122), de återstående 600 raderna är de kommande 600 dagarna , alltså endast 1 post per datum.
Denna fråga:
SELECT *
FROM T
WHERE A = '20130122';
Kommer att ge en annan utförandeplan för att:
SELECT *
FROM T
WHERE A = '20130123';
Eftersom statistiken kommer att indikera att för de första 400 av 1 000 raderna kommer att returneras, bör optimeraren inse att en tabellsökning kommer att vara effektivare än en bokmärkessökning, medan den andra bara kommer att ge 1 rader, så en bokmärkessökning kommer att vara mycket effektivare.
Nu, tillbaka till din fråga, om vi gjorde detta till en procedur:
CREATE PROCEDURE dbo.GetFromT @Param DATE
AS
SELECT *
FROM T
WHERE A = @Param
Kör sedan
EXECUTE dbo.GetFromT '20130122'; --400 rows
Frågeplanen med tabellskanningen kommer att användas, om första gången du kör den använder du '20130123' som parameter kommer den att lagra bokmärkesuppslagsplanen. Tills förfarandet är omkompilerat kommer planen att förbli densamma. Att göra något så här:
CREATE PROCEDURE dbo.GetFromT @Param VARCHAR(5)
AS
DECLARE @Param2 VARCHAR(5) = @Param;
SELECT *
FROM T
WHERE A = @Param2
Sedan körs detta:
EXECUTE dbo.GetFromT '20130122';
Även om proceduren kompileras på en gång, flyter den inte ordentligt, så frågeplanen som skapades vid den första kompileringen har ingen aning om att @Param2 kommer att bli samma som @param, så optimeraren (utan kunskap om hur många rader som ska förvänta) kommer att anta att 300 kommer att returneras (30%), eftersom en tabellsökning kommer att anses vara effektivare än en bokmärkessökning. Om du körde samma procedur med '20130123' som parameter skulle det ge samma plan (oavsett vilken parameter den först anropades med) eftersom statistiken inte kan användas för ett okänt värde. Så att köra den här proceduren för '20130122' skulle vara effektivare, men för alla andra värden skulle det vara mindre effektivt än utan lokala parametrar (förutsatt att proceduren utan lokala parametrar först anropades med allt annat än '20130122')
Några frågor att demonstrera så att du själv kan se genomförandeplaner
Skapa schema och exempeldata
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 (visar faktisk utförandeplan):
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 kommer att se det första gången GetFromT
är kompilerad använder den en tabellsökning och behåller denna när den körs med parametern '20130122', GetFromT2
använder också en tabellskanning och behåller planen för '20130122'.
Efter att procedurerna har ställts in för omkompilering och körs igen (notera i en annan ordning), GetFromT
använder en bokmärkesloopup och behåller planen för '20130122', trots att man tidigare ansett att en tabellskanning är en mer lämplig plan. GetFromT2
är opåverkad av ordern och har samma plan som före återuppfyllelsen.
Så sammanfattningsvis beror det på fördelningen av dina data och dina index, din frekvens av omkompilering och lite tur om huruvida en procedur kommer att dra nytta av att använda lokala variabler. Det gör det verkligen inte alltid hjälp.
Förhoppningsvis har jag belyst effekten av att använda lokala parametrar, exekveringsplaner och kompilering av lagrade procedurer. Om jag har misslyckats helt eller missat en nyckelpunkt kan en mycket mer djupgående förklaring hittas här:
http://www.sommarskog.se/query-plan-mysteries.html