Indeks SEEK tidak digunakan kecuali OPTION (RECOMPILE)?

11

(Pertanyaan pindah dari SO)

Saya punya tabel (data dummy) dengan indeks berkelompok berisi 2 kolom:

masukkan deskripsi gambar di sini

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:

masukkan deskripsi gambar di sini

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:

masukkan deskripsi gambar di sini

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,1lalu 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?

Membuat skrip tabel + data lengkap

Royi Namir
sumber
2
Mari kita lanjutkan diskusi ini dalam obrolan .
ypercubeᵀᴹ

Jawaban:

10

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 blok
  • OPTION (RECOMPILE)
  • SQL dinamis menggunakan 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 blok

Untuk menggambarkan IFsolusi blok untuk kasus spesifik dalam pertanyaan:

IF @productid IS NOT NULL AND @priceid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.productID = @productid
        AND T.priceID = @priceid;
END;
ELSE IF @productid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.productID = @productid;
END;
ELSE IF @priceid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.priceID = @priceid;
END;
ELSE
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T;
END;

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 FORpetunjuk 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:

SELECT
    T.productID,
    T.priceID
FROM dbo.Transactions AS T
WHERE
    (T.productID = @productid OR @productid IS NULL)
    AND (T.priceID = @priceid OR @priceid IS NULL)
OPTION (RECOMPILE);

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

Paul White 9
sumber