Cari predikat tidak menggunakan semua kolom yang tersedia

8

Saya memiliki masalah kompilasi permintaan aneh yang sulit untuk direproduksi. Ini hanya terjadi di bawah beban tinggi dan tidak dapat dengan mudah diulang.

  • Ada tabel T dengan kolom A, B, C, D.
  • Ada indeks cluster yang tidak unik pada T (A, B, C, D).
  • Ada query SELECT * DARI T DI MANA A = @ P1 DAN B = @ P2 DAN (C = @ P3 ATAU C = @ P4) DAN D = @ P5. Kondisi pencarian ada di semua kolom indeks berkerumun, kolom ke-3 memiliki OR.

Masalahnya adalah bahwa rencana kueri untuk kueri ini hanya mencari Predikat pada A dan B! Predikat pada C dan D adalah predikat biasa, jadi ini berarti bahwa pohon pencarian pada kolom C dan D tidak digunakan.

Tipe data untuk semua parameter cocok dengan tipe data kolom.

Adakah yang bisa memberikan petunjuk mengapa ini bisa terjadi? Versi SQL adalah 2008 R2 (SP1) - 10.50.2789.0 (X64)


sumber
Apakah Anda pernah benar-benar mendapatkan rencana untuk kueri parameterised yang melakukan pencarian kesetaraan pada keempat kolom? Jika demikian, apakah Anda menggunakan OPTION (RECOMPILE)?
Martin Smith

Jawaban:

8

Untuk kueri parameterised, ia tidak bisa hanya melakukan dua pencarian saja

WHERE A=@P1 AND B=@P2 AND C=@P3 AND D=@P5 

dan

WHERE A=@P1 AND B=@P2 AND C=@P4 AND D=@P5 

Karena jika @P3 = @P4itu salah akan mengembalikan baris duplikat. Jadi perlu operator yang menghapus duplikat dari ini terlebih dahulu.

Dari tes cepat akhir ini tampaknya tergantung pada ukuran tabel apakah Anda mendapatkannya atau tidak. Dalam tes di bawah 245/ 246baris adalah titik cut off antara rencana (ini juga merupakan titik cut off antara indeks yang cocok semua pada satu halaman dan itu menjadi 2 halaman daun dan halaman root).

CREATE TABLE T(A INT,B INT,C INT,D INT)

INSERT INTO T
SELECT TOP (245) 1,2,3,5
FROM master..spt_values v1

CREATE CLUSTERED INDEX IX ON T(A, B, C, D)

SELECT index_level,page_count, record_count
FROM sys.dm_db_index_physical_stats(db_id(),object_id('T'),1,NULL, 'DETAILED')

DECLARE @C1 INT = 3,
        @C2 INT = 4

 SELECT * FROM T WHERE A=1 AND B=2 AND (C=@C1 OR C=@C2) AND D=5

 DROP TABLE T

1 Halaman / 245 baris

Rencana ini memiliki pencarian A=1 AND B=2dengan predikat residu aktif(C=@C1 OR C=@C2) AND D=5

Paket 1

2 lembar daun / 246 baris

Paket 2

Dalam paket kedua, operator tambahan bertanggung jawab untuk menghapus duplikat dari yang @C1,@C2pertama sebelum melakukan pencarian.

Pencarian dalam rencana kedua sebenarnya adalah kisaran pencarian antara A=1 AND B=2 AND C > Expr1010dan A=1 AND B=2 AND C < Expr1011dengan predikat residu aktif D=5. Itu masih bukan pencarian kesetaraan di semua 4 kolom. Informasi lebih lanjut tentang operator paket tambahan dapat ditemukan di sini .

Menambahkan OPTION (RECOMPILE)memang memungkinkan untuk memeriksa nilai parameter untuk duplikat pada waktu kompilasi dan menghasilkan rencana dengan dua upaya kesetaraan.

Anda juga bisa mencapainya dengan

;WITH CTE
     AS (SELECT DISTINCT ( C )
         FROM   (VALUES (@C1),
                        (@C2)) V(C))
SELECT CA.*
FROM   CTE
       CROSS APPLY (SELECT *
                    FROM   T
                    WHERE A=1 AND B=2 AND D=5  AND C = CTE.C) CA

Paket 3

Tetapi sebenarnya dalam kasus uji ini kemungkinan akan menjadi kontra produktif karena memiliki dua berusaha ke dalam indeks halaman tunggal daripada satu meningkatkan IO logis.

Martin Smith
sumber
1
Apakah beberapa pengujian pada pertanyaan ini tadi malam sebelum Anda menambahkan komentar pertama Anda. Saya datang sejauh melihat perilaku tetapi tidak mendapatkan apa yang menyebabkannya (@ P3 = @ P4) jadi +1 untuk itu (sudah melakukannya). Saya pikir a select .. union select ...juga akan memberi Anda dua upaya ditambah langkah tambahan untuk menghapus duplikat dari hasilnya.
Mikael Eriksson
1
@MikaelEriksson - Tapi SELECT * FROM T WHERE A=1 AND B=2 AND C=@C1 AND D=5 UNION SELECT * FROM T WHERE A=1 AND B=2 AND C=@C2 AND D=5bisa salah menghapus duplikat yang harus dikembalikan. Dalam contoh data saya di mana saya malas mengisi semua dengan nilai yang sama itu akan mengembalikan 1 baris tidak256
Martin Smith
2
Ah iya. Dan OP bahkan menentukan bahwa kuncinya adalah non-unik. Beruntung saya, saya tidak mencoba menjawab yang satu ini :).
Mikael Eriksson
4

Sepenuhnya setuju dengan analisis Martin. Kueri ini tidak dapat menghasilkan pencarian pada semua 4 kolom karena predikat ATAU, kecuali (mungkin) dengan OPTION (RECOMPILE). Saya menduga ini adalah pertanyaan jarum-in-a-tumpukan jerami, Anda mungkin tidak ingin overhead tambahan.

Bagaimana dengan ini:

IF @P3=@P4
    SELECT * FROM T WHERE A=@P1 AND B=@P2 AND C=@P3 AND D=@P5.
ELSE
    SELECT * FROM T WHERE A=@P1 AND B=@P2 AND C=@P3 AND D=@P5.
    UNION ALL
    SELECT * FROM T WHERE A=@P1 AND B=@P2 AND C=@P4 AND D=@P5.

Saya tidak menguji ini tetapi bagian lain harus memberikan 2 berusaha pada semua 4 nilai dan penyatuan murah melalui penggabungan. Dalam kasus ketika keduanya berusaha berakhir di halaman yang sama saya tidak berpikir bahwa membaca halaman ekstra logis akan menambah banyak waktu Namun, tergantung pada data Anda, keduanya berusaha sangat mungkin berada di halaman yang berbeda.

Vlad G.
sumber