(Pertanyaan pindah dari SO)
Saya punya tabel (data dummy) dengan indeks berkelompok berisi 2 kolom:
Sekarang saya menjalankan dua pertanyaan itu:
declare
@productid int =1 ,
@priceid int = 1
SELECT productid,
t.priceID
FROM Transactions AS t
WHERE (productID = @productid OR @productid IS NULL)
AND (priceid = @priceid OR @priceid IS NULL)
SELECT productid,
t.priceID
FROM Transactions AS t
WHERE (productID = @productid)
AND (priceid = @priceid)
Rencana eksekusi aktual untuk kedua kueri adalah:
Seperti yang Anda lihat, yang pertama menggunakan SCAN sedangkan yang kedua menggunakan SEEK.
Namun - menambahkan OPTION (RECOMPILE)
permintaan pertama, membuat rencana eksekusi juga menggunakan SEEK:
Teman-teman di obrolan DBA memberi tahu saya bahwa:
Dalam permintaan Anda, @ productid = 1, yang berarti bahwa (productID = @ productID ATAU @productID IS NULL) dapat disederhanakan menjadi (productID = @ productID). Yang pertama membutuhkan pemindaian untuk bekerja dengan nilai @productID, yang terakhir bisa menggunakan pencarian. Jadi, ketika Anda menggunakan RECOMPILE, SQL Server akan melihat nilai apa yang sebenarnya Anda miliki di @productID dan membuat rencana terbaik untuk itu. Dengan nilai non-null di @productID, pencarian adalah yang terbaik. Jika nilai @productID tidak diketahui, paket harus sesuai dengan nilai yang memungkinkan di @productID, yang akan membutuhkan pemindaian. Diperingatkan: OPSI (RECOMPILE) akan memaksa kompilasi ulang rencana setiap kali Anda menjalankannya, yang akan menambahkan beberapa milidetik untuk setiap eksekusi. Padahal ini hanya masalah jika kueri berjalan sangat sering.
Juga:
Jika @productID adalah null, untuk nilai apa yang Anda cari? Jawab: tidak ada yang bisa dicari. Semua nilai memenuhi syarat.
Saya mengerti bahwa OPTION (RECOMPILE)
memaksa SQL Server untuk melihat apa nilai aktual dari parameter, dan melihat apakah itu bisa MENCARI dengan itu.
Tapi sekarang saya kehilangan manfaat dari kompilasi di depan.
Pertanyaan
IMHO - SCAN hanya akan terjadi jika param adalah null.
Tidak apa-apa - biarkan SQL SERVER membuat rencana eksekusi untuk SCAN.
TETAPI jika SQL Server melihat bahwa saya menjalankan kueri ini berkali-kali dengan nilai:, 1,1
lalu mengapa ia tidak membuat rencana eksekusi LAIN dan menggunakan MENCARI untuk itu?
AFAIK - SQL membuat rencana eksekusi untuk kueri yang paling banyak ditemui .
Mengapa SQL SERVER tidak menyimpan rencana eksekusi untuk:
@productid int =1 , @priceid int = 1
(Saya menjalankannya berkali-kali dengan nilai-nilai itu)
- Apakah mungkin untuk memaksa SQL untuk menjaga rencana eksekusi itu (yang menggunakan SEEK) - untuk permohonan di masa mendatang?
sumber
Jawaban:
Ringkas beberapa poin utama dari diskusi ruang obrolan kami :
Secara umum, SQL Server membuat cache satu paket untuk setiap pernyataan . Paket itu harus valid untuk semua kemungkinan nilai parameter di masa depan .
Tidak mungkin untuk me-cache rencana pencarian untuk permintaan Anda, karena paket itu tidak akan valid jika, misalnya, @productid adalah nol.
Dalam beberapa rilis mendatang, SQL Server mungkin mendukung rencana tunggal yang secara dinamis memilih antara pemindaian dan pencarian, tergantung pada nilai parameter runtime, tapi itu bukan sesuatu yang kita miliki saat ini.
Kelas masalah umum
Kueri Anda adalah contoh pola yang disebut sebagai permintaan "tangkap semua" atau "pencarian dinamis". Ada berbagai solusi, masing-masing dengan kelebihan dan kekurangan mereka sendiri. Dalam versi modern SQL Server (2008+), opsi utamanya adalah:
IF
blokOPTION (RECOMPILE)
sp_executesql
Karya paling komprehensif tentang topik ini mungkin oleh Erland Sommarskog, yang termasuk dalam referensi di akhir jawaban ini. Tidak ada jalan keluar dari kerumitan yang terlibat, sehingga perlu menginvestasikan waktu untuk mencoba setiap opsi untuk memahami trade-off dalam setiap kasus.
IF
blokUntuk menggambarkan
IF
solusi blok untuk kasus spesifik dalam pertanyaan:Ini berisi pernyataan terpisah untuk empat kemungkinan kasus null-atau-bukan-nol untuk masing-masing dari dua parameter (atau variabel lokal), jadi ada empat rencana.
Ada masalah potensial di sana dengan sniffing parameter, yang mungkin memerlukan
OPTIMIZE FOR
petunjuk pada setiap permintaan. Silakan lihat bagian referensi untuk mengeksplorasi tipe seluk beluk ini.Kompilasi ulang
Seperti disebutkan di atas dan dalam pertanyaan, Anda juga bisa menambahkan
OPTION (RECOMPILE)
petunjuk untuk mendapatkan rencana baru (mencari atau memindai) pada setiap permintaan. Mengingat frekuensi panggilan yang relatif lambat dalam kasus Anda (rata-rata sekali setiap sepuluh detik, dengan waktu kompilasi sub-milidetik) sepertinya opsi ini akan cocok untuk Anda:Dimungkinkan juga untuk menggabungkan fitur-fitur dari opsi-opsi di atas dengan cara-cara kreatif, untuk memanfaatkan sebaik-baiknya setiap metode, sambil meminimalkan kelemahannya. Benar-benar tidak ada jalan pintas untuk memahami hal-hal ini secara terperinci, kemudian membuat pilihan berdasarkan informasi yang didukung oleh pengujian realistis.
Bacaan lebih lanjut
RECOMPILE
Opsisumber