Saya tidak akan mencoba dan menjelaskan detail lengkap dari parameter sniffing, tetapi singkatnya, tidak selalu membantu (dan itu dapat menghalangi).
Bayangkan sebuah tabel (T) dengan kunci utama dan kolom Tanggal yang diindeks (A), di dalam tabel ada 1.000 baris, 400 memiliki nilai A yang sama (katakanlah hari ini 20130122), 600 baris sisanya adalah 600 hari ke depan , jadi hanya 1 catatan per tanggal.
Kueri ini:
SELECT *
FROM T
WHERE A = '20130122';
Akan menghasilkan rencana eksekusi yang berbeda untuk:
SELECT *
FROM T
WHERE A = '20130123';
Karena statistik akan menunjukkan bahwa untuk 400 dari 1.000 baris pertama akan dikembalikan, pengoptimal harus menyadari bahwa pemindaian tabel akan lebih efisien daripada pencarian bookmark, sedangkan yang kedua hanya akan menghasilkan 1 baris, jadi pencarian bookmark akan banyak lebih efisien.
Sekarang, kembali ke pertanyaan Anda, jika kami membuat prosedur ini:
CREATE PROCEDURE dbo.GetFromT @Param DATE
AS
SELECT *
FROM T
WHERE A = @Param
Kemudian jalankan
EXECUTE dbo.GetFromT '20130122'; --400 rows
Paket kueri dengan pemindaian tabel akan digunakan, jika pertama kali Anda menjalankannya menggunakan '20130123' sebagai parameter, paket pencarian bookmark akan disimpan. Sampai saat prosedur dikompilasi ulang, rencananya akan tetap sama. Melakukan sesuatu seperti ini:
CREATE PROCEDURE dbo.GetFromT @Param VARCHAR(5)
AS
DECLARE @Param2 VARCHAR(5) = @Param;
SELECT *
FROM T
WHERE A = @Param2
Kemudian ini dijalankan:
EXECUTE dbo.GetFromT '20130122';
Sementara prosedur dikompilasi sekaligus, itu tidak mengalir dengan benar, sehingga rencana kueri yang dibuat pada kompilasi pertama tidak tahu bahwa @Param2 akan menjadi sama dengan @param, jadi pengoptimal (tanpa mengetahui berapa banyak baris yang harus harapkan) akan menganggap 300 akan dikembalikan (30%), karena itu akan menganggap pemindaian tabel lebih efisien daripada pencarian bookmark. Jika Anda menjalankan prosedur yang sama dengan '20130123' sebagai parameter, itu akan menghasilkan rencana yang sama (terlepas dari parameter apa yang pertama kali dipanggil) karena statistik tidak dapat digunakan untuk nilai yang tidak diketahui. Jadi menjalankan prosedur ini untuk '20130122' akan lebih efisien, tetapi untuk semua nilai lainnya akan kurang efisien dibandingkan tanpa parameter lokal (dengan asumsi prosedur tanpa parameter lokal pertama kali dipanggil dengan apa pun kecuali '20130122')
Beberapa kueri untuk ditunjukkan sehingga Anda dapat melihat sendiri rencana eksekusi
Buat skema dan contoh 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
Jalankan prosedur (menunjukkan rencana eksekusi yang sebenarnya):
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';
Anda akan melihatnya pertama kali GetFromT
dikompilasi menggunakan pemindaian tabel, dan menyimpannya saat dijalankan dengan parameter '20130122', GetFromT2
juga menggunakan pemindaian tabel dan mempertahankan paket untuk '20130122'.
Setelah prosedur telah ditetapkan untuk kompilasi ulang dan dijalankan kembali (perhatikan dalam urutan yang berbeda), GetFromT
menggunakan loopup bookmark, dan mempertahankan rencana untuk '20130122', meskipun sebelumnya menganggap bahwa pemindaian tabel adalah rencana yang lebih tepat. GetFromT2
tidak terpengaruh oleh perintah dan memiliki rencana yang sama seperti sebelum recompliation.
Jadi, secara ringkas, itu tergantung pada distribusi data Anda, dan indeks Anda, frekuensi kompilasi ulang Anda, dan sedikit keberuntungan, apakah suatu prosedur akan mendapat manfaat dari penggunaan variabel lokal. Tentu saja tidak selalu bantuan.
Mudah-mudahan saya telah menjelaskan efek penggunaan parameter lokal, rencana eksekusi dan kompilasi prosedur tersimpan. Jika saya gagal total, atau melewatkan poin kunci, penjelasan yang lebih mendalam dapat ditemukan di sini:
http://www.sommarskog.se/query-plan-mysteries.html