Nie będę próbował wyjaśniać wszystkich szczegółów dotyczących wąchania parametrów, ale krótko mówiąc, nie, nie zawsze pomoc (i może przeszkadzać).
Wyobraź sobie tabelę (T) z kluczem podstawowym i indeksowaną kolumną Data (A), w tabeli jest 1000 wierszy, 400 ma taką samą wartość A (powiedzmy dzisiaj 20130122), pozostałe 600 wierszy to następne 600 dni , więc tylko 1 rekord na datę.
To zapytanie:
SELECT *FROM TWHERE A ='20130122';
Daje inny plan wykonania, aby:
SELECT *FROM TWHERE A ='20130123';
Ponieważ statystyki wskażą, że dla pierwszych 400 z 1000 wierszy zostanie zwróconych, optymalizator powinien rozpoznać, że skanowanie tabeli będzie bardziej wydajne niż wyszukiwanie zakładek, podczas gdy drugie przyniesie tylko 1 wiersz, więc wyszukiwanie zakładek będzie znacznie bardziej wydajny.
Wracając do twojego pytania, jeśli zrobiliśmy z tej procedury:
CREATE PROCEDURE dbo.GetFromT @Param DATEAS SELECT * FROM T WHERE A =@Param
Następnie uruchom
WYKONAJ dbo.GetFromT '20130122'; --400 wierszy
Zostanie użyty plan zapytania ze skanowaniem tabeli, jeśli przy pierwszym uruchomieniu użyjesz '20130123' jako parametru, będzie on przechowywać plan wyszukiwania zakładek. Do czasu ponownego opracowania procedury plan pozostanie taki sam. Robisz coś takiego:
CREATE PROCEDURE dbo.GetFromT @Param VARCHAR(5)AS DECLARE @Param2 VARCHAR(5) =@Param; SELECT * FROM T WHERE A =@Param2
Następnie to jest uruchamiane:
WYKONAJ dbo.GetFromT '20130122';
Chociaż procedura jest kompilowana za jednym razem, nie przebiega prawidłowo, więc plan zapytań utworzony przy pierwszej kompilacji nie ma pojęcia, że @Param2 stanie się tym samym co @param, a więc optymalizatorem (bez wiedzy o tym, ile wierszy ma oczekiwać) przyjmie, że zostanie zwrócone 300 (30%), co oznacza, że skanowanie tabeli będzie bardziej wydajne niż wyszukiwanie zakładek. Jeśli uruchomisz tę samą procedurę z parametrem „20130123” jako parametr, uzyskasz ten sam plan (niezależnie od tego, z jakim parametrem został on wywołany po raz pierwszy), ponieważ statystyki nie mogą być użyte dla wartości niekonnwn. Zatem uruchomienie tej procedury dla „20130122” byłoby bardziej wydajne, ale dla wszystkich innych wartości byłoby mniej wydajne niż bez parametrów lokalnych (zakładając, że procedura bez parametrów lokalnych została najpierw wywołana z czymkolwiek innym niż „20130122”)
Niektóre zapytania do zademonstrowania, abyś mógł zobaczyć plany wykonania dla siebie
Utwórz schemat i przykładowe dane
Uruchom procedury (pokazując rzeczywisty plan wykonania):
EXECUTE GetFromT '20130122';EXECUTE GetFromT '20130123';EXECUTE GetFromT2 '20130122';EXECUTE GetFromT2 '20130123';GOEXECUTE SP_RECOMPILE GetFromT;EXECUTE SP_RECOMPIUTET'; EXECUTE GetFromT2 '20130123';EXECUTE GetFromT2 '20130122';
Zobaczysz, że za pierwszym razem GetFromT
jest skompilowany używa skanowania tabeli i zachowuje to po uruchomieniu z parametrem '20130122', GetFromT2
używa również skanowania tabeli i zachowuje plan dla „20130122”.
Po ustawieniu procedur do ponownej kompilacji i ponownym uruchomieniu (zanotuj w innej kolejności), GetFromT
używa pętli zakładek i zachowuje plan dla „20130122”, mimo że wcześniej uznano, że skanowanie tabeli jest bardziej odpowiednim planem. GetFromT2
nie ma wpływu na zamówienie i ma taki sam plan jak przed ponownym wypełnieniem.
Podsumowując, zależy to od dystrybucji danych i indeksów, częstotliwości rekompilacji i odrobiny szczęścia, czy procedura skorzysta na użyciu zmiennych lokalnych. Z pewnością nie zawsze pomoc.
Mam nadzieję, że rzuciłem nieco światła na efekt używania parametrów lokalnych, planów wykonania i kompilacji procedur składowanych. Jeśli całkowicie zawiodłem lub przegapiłem kluczową kwestię, znacznie bardziej szczegółowe wyjaśnienie można znaleźć tutaj:
http://www.sommarskog.se/query-plan-mysteries.html