Ketika saya menjalankan kode berikut ini, dibutuhkan 22,5 menit dan membaca 106 juta. Namun, jika saya menjalankan hanya pernyataan pilih dalam dengan sendirinya hanya membutuhkan waktu 15 detik dan membaca 264k. Sebagai catatan tambahan, kueri pemilihan tidak mengembalikan catatan.
Adakah yang tahu mengapa itu IF EXISTS
akan membuatnya berjalan lebih lama dan melakukan lebih banyak bacaan? Saya juga mengubah pernyataan pilih untuk dilakukan SELECT TOP 1 [dlc].[id]
dan saya membunuhnya setelah 2 menit.
Sebagai perbaikan sementara, saya telah mengubahnya untuk melakukan penghitungan (*) dan menetapkan nilai itu ke variabel @cnt
. Kemudian ia membuat IF 0 <> @cnt
pernyataan. Tapi saya pikir EXISTS
akan lebih baik, karena jika ada catatan yang dikembalikan dalam pernyataan pilih itu akan berhenti melakukan pemindaian / mencari setelah menemukan setidaknya satu catatan, sedangkan count(*)
akan menyelesaikan permintaan penuh. Apa yang saya lewatkan?
IF EXISTS
(SELECT [dlc].[ID]
FROM TableDLC [dlc]
JOIN TableD [d]
ON [d].[ID] = [dlc].[ID]
JOIN TableC [c]
ON [c].[ID] = [d].[ID2]
WHERE [c].[Name] <> [dlc].[Name])
BEGIN
<do something>
END
sumber
IF NOT EXISTS (...) BEGIN END ELSE BEGIN <do something> END
.Jawaban:
Seperti yang saya jelaskan dalam jawaban saya untuk pertanyaan terkait ini:
Bagaimana (dan mengapa) TOP mempengaruhi rencana eksekusi?
Menggunakan
EXISTS
memperkenalkan tujuan baris, di mana pengoptimal menghasilkan rencana eksekusi yang bertujuan untuk menemukan baris pertama dengan cepat. Dalam melakukan ini, diasumsikan bahwa data terdistribusi secara seragam. Misalnya, jika statistik menunjukkan ada 100 pertandingan yang diharapkan dalam 100.000 baris, itu akan menganggap itu harus membaca hanya 1.000 baris untuk menemukan pertandingan pertama.Ini akan menghasilkan waktu eksekusi yang lebih lama dari yang diharapkan jika asumsi ini ternyata salah. Misalnya, jika SQL Server memilih metode akses (mis. Pemindaian tidak berurutan) yang terjadi untuk menemukan nilai pencocokan pertama sangat terlambat dalam pencarian, itu bisa menghasilkan pemindaian yang hampir lengkap. Di sisi lain, jika baris yang cocok ditemukan di antara beberapa baris pertama, kinerja akan sangat baik. Ini adalah risiko mendasar dengan sasaran baris - kinerja yang tidak konsisten.
Biasanya dimungkinkan untuk merumuskan ulang kueri sedemikian rupa sehingga sasaran baris tidak ditetapkan. Tanpa sasaran baris, kueri masih dapat berakhir ketika baris pertama yang cocok ditemukan (jika ditulis dengan benar), tetapi strategi rencana eksekusi kemungkinan akan berbeda (dan mudah-mudahan, lebih efektif). Jelas, hitung (*) akan membutuhkan membaca semua baris, jadi itu bukan alternatif yang sempurna.
Jika Anda menjalankan SQL Server 2008 R2 atau yang lebih baru, Anda juga dapat secara umum menggunakan flag jejak yang didokumentasikan dan didukung 4138 untuk mendapatkan rencana eksekusi tanpa sasaran baris. Bendera ini juga dapat ditentukan menggunakan petunjuk yang didukung
OPTION (QUERYTRACEON 4138)
, meskipun perlu diketahui bahwa ini memerlukan izin sysadmin runtime , kecuali digunakan dengan panduan paket.Sayangnya
Tidak satu pun di atas yang berfungsi dengan
IF EXISTS
pernyataan bersyarat. Ini hanya berlaku untuk DML biasa. Ini akan bekerja denganSELECT TOP (1)
formulasi alternatif yang Anda coba. Itu mungkin lebih baik daripada menggunakanCOUNT(*)
, yang harus menghitung semua baris yang memenuhi syarat, seperti yang disebutkan sebelumnya.Yang mengatakan, ada sejumlah cara untuk mengekspresikan persyaratan ini yang akan memungkinkan Anda untuk menghindari atau mengendalikan tujuan baris, sambil menghentikan pencarian lebih awal. Satu contoh terakhir:
sumber
Karena EXIS hanya perlu menemukan satu baris, itu akan menggunakan tujuan baris. Ini terkadang menghasilkan rencana yang kurang ideal. Jika Anda mengharapkannya akan seperti itu untuk Anda, isi sebuah variabel dengan hasil dari
COUNT(*)
dan kemudian uji variabel itu untuk melihat apakah itu lebih dari 0.Jadi ... Dengan sasaran baris kecil, ia akan menghindari operasi pemblokiran, seperti membangun tabel hash, atau menyortir aliran yang dapat berguna untuk menggabungkan gabungan, karena akan mengetahui bahwa ia terikat untuk menemukan sesuatu dengan sangat cepat, dan oleh karena itu loop bersarang akan lebih baik jika ia menemukan sesuatu. Kecuali bahwa ini dapat membuat rencana yang jauh lebih buruk di seluruh set. Jika menemukan satu baris cepat, Anda ingin metode ini untuk menghindari blok ...
sumber