Nebudu se pokoušet vysvětlovat úplné detaily sniffování parametrů, ale zkrátka ne vždy pomoci (a může překážet).
Představte si tabulku (T) s primárním klíčem a indexovaným sloupcem Datum (A), v tabulce je 1000 řádků, 400 má stejnou hodnotu A (řekněme dnes 20130122), zbývajících 600 řádků je dalších 600 dní , takže pouze 1 záznam na datum.
Tento dotaz:
SELECT *
FROM T
WHERE A = '20130122';
Výsledkem bude jiný plán provádění než:
SELECT *
FROM T
WHERE A = '20130123';
Protože statistiky naznačují, že pro prvních 400 z 1 000 řádků bude vráceno, měl by optimalizátor uznat, že skenování tabulky bude efektivnější než vyhledávání záložek, zatímco druhé poskytne pouze 1 řádek, takže vyhledávání záložek bude mnohem efektivnější.
Nyní zpět k vaší otázce, pokud jsme z toho udělali postup:
CREATE PROCEDURE dbo.GetFromT @Param DATE
AS
SELECT *
FROM T
WHERE A = @Param
Pak spusťte
EXECUTE dbo.GetFromT '20130122'; --400 rows
Použije se plán dotazů se skenováním tabulky, pokud při prvním spuštění použijete jako parametr '20130123', uloží se plán vyhledávání záložek. Dokud nebude postup překompilován, plán zůstane stejný. Dělat něco takového:
CREATE PROCEDURE dbo.GetFromT @Param VARCHAR(5)
AS
DECLARE @Param2 VARCHAR(5) = @Param;
SELECT *
FROM T
WHERE A = @Param2
Poté se spustí toto:
EXECUTE dbo.GetFromT '20130122';
I když je procedura zkompilována najednou, neprobíhá správně, takže plán dotazů vytvořený při první kompilaci netuší, že @Param2 se stane stejným jako @param, takže optimalizátor (bez znalosti počtu řádků očekávat) bude předpokládat, že se vrátí 300 (30 %), protože takový bude považovat skenování tabulky za efektivnější než vyhledávání záložek. Pokud byste spustili stejnou proceduru s parametrem „20130123“, přineslo by to stejný plán (bez ohledu na to, s jakým parametrem byl poprvé vyvolán), protože statistiku nelze použít pro neznámou hodnotu. Spuštění této procedury pro „20130122“ by tedy bylo efektivnější, ale pro všechny ostatní hodnoty by bylo méně efektivní než bez lokálních parametrů (za předpokladu, že procedura bez lokálních parametrů byla nejprve vyvolána s čímkoli jiným než „20130122“)
Některé dotazy k demonstraci, abyste si sami mohli prohlédnout plány provádění
Vytvořte schéma a ukázková data
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
Spustit procedury (zobrazující skutečný plán provádění):
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';
Uvidíte, že poprvé GetFromT
je zkompilován, používá skenování tabulek a zachovává to při spuštění s parametrem '20130122', GetFromT2
také používá skenování tabulky a zachovává plán pro '20130122'.
Po nastavení procedur pro rekompilaci a opětovném spuštění (poznamenejte si v jiném pořadí), GetFromT
používá smyčku záložek a zachovává plán pro '20130122', přestože se dříve domníval, že skenování tabulky je vhodnější plán. GetFromT2
není objednávkou ovlivněna a má stejný plán jako před kompliací.
Stručně řečeno, záleží na distribuci vašich dat a vašich indexů, frekvenci rekompilace a troše štěstí, zda bude procedura těžit z použití lokálních proměnných. Rozhodně to není vždy pomoc.
Doufám, že jsem trochu osvětlil účinek použití místních parametrů, plánů provádění a kompilace uložených procedur. Pokud jsem úplně selhal nebo jsem přehlédl klíčový bod, mnohem podrobnější vysvětlení lze nalézt zde:
http://www.sommarskog.se/query-plan-mysteries.html