Non cercherò di spiegare tutti i dettagli dello sniffing dei parametri, ma in breve, no, non è sempre aiuto (e può ostacolare).
Immagina una tabella (T) con una chiave primaria e una colonna Data indicizzata (A), nella tabella ci sono 1.000 righe, 400 hanno lo stesso valore di A (diciamo oggi 20130122), le restanti 600 righe sono i prossimi 600 giorni , quindi solo 1 record per data.
Questa domanda:
SELECT *
FROM T
WHERE A = '20130122';
Produrrà un piano di esecuzione diverso per:
SELECT *
FROM T
WHERE A = '20130123';
Poiché le statistiche indicheranno che per le prime 400 righe su 1.000 verranno restituite, l'ottimizzatore dovrebbe riconoscere che una scansione della tabella sarà più efficiente di una ricerca di segnalibri, mentre la seconda produrrà solo 1 riga, quindi una ricerca di segnalibri sarà molto più efficiente.
Ora, tornando alla tua domanda, se abbiamo reso questa una procedura:
CREATE PROCEDURE dbo.GetFromT @Param DATE
AS
SELECT *
FROM T
WHERE A = @Param
Quindi esegui
EXECUTE dbo.GetFromT '20130122'; --400 rows
Verrà utilizzato il piano di query con la scansione della tabella, se la prima volta che lo esegui si utilizza "20130123" come parametro, memorizzerà il piano di ricerca dei segnalibri. Fino a quando la procedura non sarà ricompilata, il piano rimarrà lo stesso. Fare qualcosa del genere:
CREATE PROCEDURE dbo.GetFromT @Param VARCHAR(5)
AS
DECLARE @Param2 VARCHAR(5) = @Param;
SELECT *
FROM T
WHERE A = @Param2
Quindi questo viene eseguito:
EXECUTE dbo.GetFromT '20130122';
Sebbene la procedura venga compilata in una volta sola, non scorre correttamente, quindi il piano di query creato alla prima compilazione non ha idea che @Param2 diventerà lo stesso di @param, quindi l'ottimizzatore (senza sapere quante righe aspettarsi) presupporrà che verrà restituito 300 (30%), in quanto tale riterrà una scansione della tabella più efficiente di una ricerca di un segnalibro. Se si esegue la stessa procedura con "20130123" come parametro, si otterrebbe lo stesso piano (indipendentemente dal parametro con cui è stato richiamato per la prima volta) perché le statistiche non possono essere utilizzate per un valore sconosciuto. Quindi eseguire questa procedura per '20130122' sarebbe più efficiente, ma per tutti gli altri valori sarebbe meno efficiente che senza parametri locali (supponendo che la procedura senza parametri locali sia stata prima richiamata con qualcosa di diverso da '20130122')
Alcune domande da dimostrare in modo da poter visualizzare personalmente i piani di esecuzione
Crea schema e dati di esempio
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
Procedure di esecuzione (che mostrano il piano di esecuzione effettivo):
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';
Lo vedrai la prima volta GetFromT
viene compilato utilizza una scansione della tabella e la conserva quando viene eseguita con il parametro '20130122', GetFromT2
utilizza anche una scansione della tabella e conserva il piano per "20130122".
Dopo che le procedure sono state impostate per la ricompilazione ed eseguite nuovamente (notare in un ordine diverso), GetFromT
utilizza un loopup di segnalibri e mantiene il piano per "20130122", nonostante in precedenza abbia ritenuto che una scansione della tabella sia un piano più appropriato. GetFromT2
non è interessato dall'ordine e ha lo stesso piano di prima della riconversione.
Quindi, in sintesi, dipende dalla distribuzione dei tuoi dati e dai tuoi indici, dalla frequenza di ricompilazione e da un po' di fortuna se una procedura trarrà vantaggio dall'utilizzo di variabili locali. Certamente non sempre aiuto.
Spero di aver fatto luce sull'effetto dell'utilizzo di parametri locali, piani di esecuzione e compilazione di stored procedure. Se ho fallito completamente o ho perso un punto chiave, una spiegazione molto più approfondita può essere trovata qui:
http://www.sommarskog.se/query-plan-mysteries.html