Membungkus kueri dalam JIKA ADA membuatnya sangat lambat

16

Saya punya pertanyaan di bawah ini:

select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)

Kueri di atas selesai dalam tiga detik.

Jika kueri di atas mengembalikan nilai apa pun, kami ingin prosedur tersimpan EXIT, jadi saya menulis ulang seperti di bawah ini:

If Exists(
select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
)
Begin
Raiserror('Source missing',16,1)
Return
End

Namun ini membutuhkan waktu 10 menit.

Saya dapat menulis ulang kueri di atas seperti di bawah ini, yang juga selesai dalam waktu kurang dari 3 detik:

  select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source
if @@rowcount >0
Begin
Raiserror('Source missing',16,1)
Return
End

Masalah dengan penulisan ulang di atas adalah bahwa kueri di atas adalah bagian dari prosedur tersimpan yang lebih besar dan mengembalikan beberapa set hasil. Dalam C #, kita mengulangi setiap set hasil dan melakukan beberapa pemrosesan.

Di atas mengembalikan set hasil kosong, jadi jika saya pergi dengan pendekatan ini, saya harus mengubah C # dan melakukan penyebaran lagi.

Jadi pertanyaan saya adalah,

mengapa menggunakan hanya IF EXISTSmengubah rencana untuk mengambil begitu banyak waktu?

Di bawah ini adalah detail yang dapat membantu Anda dan beri tahu saya jika Anda membutuhkan detail:

  1. Buat skrip tabel dan statistik untuk mendapatkan paket yang sama dengan skrip saya
  2. Rencana Eksekusi Lambat
  3. Rencana Eksekusi Cepat

    Paket lambat menggunakan Brentozar Tempel paket
    Cepat Rencana menggunakan Brentozar Tempel paket

Catatan: Kedua kueri itu sama (menggunakan parameter), satu-satunya perbedaan adalahEXISTS (saya mungkin telah membuat beberapa kesalahan saat menganonimkan).

Skrip pembuatan tabel di bawah ini:

http://pastebin.com/CgSHeqXc - statistik tabel kecil
http://pastebin.com/GUu9KfpS - statistik tabel besar

TheGameiswar
sumber
Diskusi tentang pertanyaan ini telah dipindahkan ke ruang obrolan ini .
Paul White Reinstate Monica

Jawaban:

18

Seperti yang telah dijelaskan oleh Paul White dalam posting blognya: Inside the Optimizer: Row Goals In Depth , EXISTSmemperkenalkan tujuan baris, yang lebih disukai NESTED LOOPSatau MERGE JOINlebihHASH MATCH

Sebagai contoh terakhir, pertimbangkan bahwa semi-join logis (seperti sub-kueri yang diperkenalkan dengan EXISTS) membagikan tema keseluruhan: tema tersebut harus dioptimalkan untuk menemukan baris pencocokan pertama dengan cepat.

Dalam kueri Anda, ini tampaknya terjadi untuk memperkenalkan loop bersarang dan menghapus paralelisme, menghasilkan rencana yang lebih lambat.

Jadi, Anda mungkin perlu menemukan cara untuk menulis ulang kueri Anda tanpa menggunakan NOT EXISTSdari kueri Anda.

Anda mungkin lolos dengan menulis ulang kueri Anda menggunakan LEFT OUTER JOINdan memeriksa tidak ada baris dalam tabel kecil dengan pengujian untukNULL

If EXISTS(
    SELECT databasename
    FROM somedb.dbo.bigtable l
    LEFT JOIN dbo.smalltable c ON c.source = l.source
    WHERE databasename = 'someval'
    AND source <> 'kt'
    AND c.source IS NULL
)

Anda mungkin dapat menggunakan EXCEPTkueri juga, tergantung pada berapa banyak bidang yang perlu Anda bandingkan seperti ini:

If EXISTS(
   SELECT source
   FROM somedb.dbo.bigtable l
   WHERE databasename = 'someval'
   AND source <> 'kt'

   EXCEPT

   SELECT source
   FROM dbo.smalltable
)

Pikiran Anda, Aaron Bertrand memiliki posting blog memberikan alasan mengapa ia lebih suka TIDAK ADA yang harus Anda baca untuk melihat apakah pendekatan lain berfungsi lebih baik, dan untuk menyadari potensi masalah ketepatan jika nilai NULL.

T & J Terkait: JIKA ADA lebih lama dari pernyataan pilih tertanam

Tom V - Tim Monica
sumber
0

Anda perlu menulis ulang kueri Anda menggunakan gabungan eksplisit dan menentukan operasi gabungan mana yang ingin Anda gunakan (loop, hash, atau gabung) seperti ini.

If not exists(
    select databasename 
    from somedb.dbo.bigtable l
    inner hash join dbo.smalltable c 
        on c.source = l.source
where databasename ='someval' and source  <>'kt')
begin
    Raiserror('Source missing',16,1)
    Return
end

Saat menggunakan EXIS atau NOT EXIS, SQL Server membuat rencana kueri dengan operasi NESTED LOOP dengan asumsi bahwa ia harus memeriksa semua baris di set satu per satu mencari baris pertama untuk memenuhi kondisi tersebut. Menggunakan HASH JOIN akan mempercepatnya.

Artem Machnev
sumber
Daripada Anda, akan mengujinya
TheGameiswar
0

Saya telah menemukan masalah yang sama, saya berhasil bekerja sendiri dengan menghindari menggunakan "EXISTS" dan dengan menggunakan fungsi "COUNT ()" dan pernyataan "JIKA ... LAIN".

Sebagai contoh Anda coba yang berikut ini:

IF
(
    SELECT
        COUNT(l.databasename) + 1 AS databasename
    FROM somedb.dbo.bigtable AS l

    WHERE   l.databasename ='someval'
        AND l.[source]  <> 'kt'
        AND NOT EXISTS(SELECT 1 FROM dbo.smalltable AS c WHERE c.[source]=l.[source])
) > 1 --Acts like EXISTS
BEGIN
    RAISERROR('Source missing', 16, 1)
RETURN
END

Alasan saya menambahkan "+ 1" ke hitungan adalah agar saya dapat menggunakan "> 1" dalam kondisi IF, menggunakan "> 0" atau "<> 0" akan memicu kueri untuk menggunakan nested loop bukan HASH Pertandingan. Belum melihat mengapa itu persis terjadi akan menarik untuk mencari tahu mengapa.

Semoga itu bisa membantu!

Hayder Nahee
sumber