Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Apakah menetapkan parameter input prosedur tersimpan ke variabel lokal membantu mengoptimalkan kueri?

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



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagaimana menemukan gaji tertinggi kelima dalam satu permintaan di SQL Server

  2. Pilih kembali daftar yang dibatasi koma yang dikelompokkan berdasarkan ID

  3. Secara otomatis menyelesaikan konflik penggabungan kunci utama

  4. Haruskah MAMP mengembalikan ::1 sebagai IP di localhost?

  5. Pemeliharaan Database Sistem SQL Server