Kinerja SQL Server DI vs. yang sudah ada

115

Saya penasaran manakah dari berikut ini yang akan lebih efisien?

Saya selalu berhati-hati dalam menggunakan INkarena saya percaya SQL Server mengubah hasil yang ditetapkan menjadi IFpernyataan besar . Untuk kumpulan hasil yang besar, ini dapat mengakibatkan kinerja yang buruk. Untuk kumpulan hasil kecil, saya tidak yakin mana yang lebih disukai. Untuk kumpulan hasil yang besar, bukankah EXISTSlebih efisien?

WHERE EXISTS (SELECT * FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

vs.

WHERE bx.BoxID IN (SELECT BoxID FROM Base WHERE [Rank = 2])
Randy Minder
sumber
8
Cara terbaik untuk mengetahuinya adalah dengan mencobanya dan melakukan beberapa pengukuran.
Klaus Byskov Pedersen
10
ada yang punya menjadi duplikat trilyun untuk ini ......
marc_s
5
@marc_s - Mungkin begitu, tetapi dalam waktu yang akan membuat saya melihat semua posting tentang subjek ini, dan menemukan satu yang sesuai dengan kasus saya, saya punya empat jawaban untuk pertanyaan saya.
Randy Minder
7
FYI jika Anda ingin yang paling cara performant, Anda dapat select 1 from Base...di Anda where existskarena Anda tidak benar-benar peduli tentang hasil, hanya saja berturut-turut benar-benar ada.
brad
2
@marc_s itu sangat menyedihkan, karena saya memang meluangkan waktu untuk melihat-lihat kiriman agar tidak menambahkan sampah lagi ke stackoverflow. Saya tidak membutuhkan jawaban yang disesuaikan untuk menyelesaikan pekerjaan saya. Itulah jenis pemikiran yang menambahkan satu juta duplikat di tempat hanya beberapa dengan jawaban yang bagus
IvoC

Jawaban:

140

EXISTS akan lebih cepat karena setelah mesin menemukan tabrakan, mesin akan berhenti melihat sebagaimana kondisi terbukti benar.

Dengan IN, itu akan mengumpulkan semua hasil dari sub-query sebelum diproses lebih lanjut.

keithwarren7
sumber
4
Itu poin yang bagus. Pernyataan IN memerlukan SQL Server untuk menghasilkan kumpulan hasil yang lengkap, dan kemudian membuat pernyataan IF yang besar menurut saya.
Randy Minder
72
Ini dulunya benar tetapi dalam versi saat ini (setidaknya 2008) pengoptimal jauh lebih pintar ... sebenarnya memperlakukan IN () seperti EXISTS ().
Aaron Bertrand
11
@ Aaron - ya, biasanya pengoptimal secara internal akan menghasilkan rencana yang lebih baik. Namun, mengandalkan pintasan internal dapat merugikan dalam skenario yang lebih kompleks.
Scott Coates
2
Ini salah. Itu di 2010 dan masih.
Magnus
2
IN dan EXISTS memiliki rencana kueri yang sama persis, dan IO. Tidak ada alasan untuk berpikir mereka berbeda dalam kinerja. periksa statistik waktu Anda dan yakinkan diri Anda
Nelssen
40

Jawaban yang diterima berpandangan pendek dan pertanyaannya agak longgar karena:

1) Tidak secara eksplisit menyebutkan apakah ada indeks penutup di kiri, kanan, atau kedua sisi.

2) Tidak ada yang memperhitungkan ukuran set sisi kiri input dan set sisi kanan input.
(Pertanyaan hanya menyebutkan kumpulan hasil yang besar secara keseluruhan ).

Saya yakin pengoptimal cukup pintar untuk mengonversi antara "dalam" vs "ada" ketika ada perbedaan biaya yang signifikan karena (1) dan (2), jika tidak, pengoptimal hanya dapat digunakan sebagai petunjuk (misalnya, ada untuk mendorong penggunaan indeks yang bisa dicari di sisi kanan).

Kedua formulir dapat dikonversi menjadi formulir gabungan secara internal, memiliki urutan gabungan dibalik, dan dijalankan sebagai loop, hash, atau penggabungan - berdasarkan perkiraan jumlah baris (kiri dan kanan) dan keberadaan indeks di kiri, kanan, atau kedua sisi.

crokusek.dll
sumber
3
tidak tahu mengapa jawaban luar biasa ini tidak mendapat perhatian lagi. Memahami indeks / struktur untuk kedua sisi bisa berdampak Saya setuju. Kata yang bagus.
SheldonH
Pengoptimal selalu memberikan rencana yang sama untuk INdan EXISTS. Coba dan temukan kasus di mana mereka tidak mendapatkan rencana yang sama (meskipun ini tidak berlaku untuk NOT INdan NOT EXISTS)
Martin Smith
@MartinSmith Saya berasumsi Anda tahu apa yang Anda bicarakan, tetapi apakah Anda punya bukti bahwa rencananya selalu sama? Jika demikian, itu akan menjernihkan ketidaksepakatan selama satu dekade di sini.
MarredCheese
@MarredCheese - tanggung jawab ada pada orang-orang yang mengklaim bahwa menghasilkan satu contoh saja adalah berbeda
Martin Smith
37

Saya telah melakukan beberapa pengujian pada SQL Server 2005 dan 2008, dan pada EXISTS dan IN kembali dengan rencana eksekusi aktual yang sama persis, seperti yang dinyatakan orang lain. Pengoptimal sudah optimal. :)

Sesuatu yang harus diperhatikan, EXISTS, IN, dan JOIN terkadang dapat memberikan hasil yang berbeda jika Anda tidak menyusun kueri dengan benar: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210 .aspx

Adam Nofsinger
sumber
5

Ada banyak jawaban jawaban yang menyesatkan di sini, termasuk jawaban yang sangat disukai (meskipun saya tidak percaya operasi mereka berarti membahayakan). Jawaban singkatnya adalah: Ini sama.

Ada banyak kata kunci dalam bahasa SQL (T-), tetapi pada akhirnya, satu-satunya hal yang benar-benar terjadi pada perangkat keras adalah operasi seperti yang terlihat dalam rencana kueri eksekusi.

Operasi relasional (teori matematika) yang kita lakukan saat kita memanggil [NOT] INdan [NOT] EXISTSmerupakan semi join (anti-join saat menggunakan NOT). Bukan kebetulan bahwa operasi sql-server yang sesuai memiliki nama yang sama . Tidak ada operasi yang menyebutkan INatau di EXISTSmana pun - hanya (anti-) semi bergabung. Dengan demikian, tidak ada cara bahwa pilihan INvs yang secara logis setara EXISTSdapat mempengaruhi kinerja karena hanya ada satu cara, operasi (anti) semi join execution, untuk mendapatkan hasilnya. .

Sebuah contoh:

Kueri 1 ( rencana )

select * from dt where dt.customer in (select c.code from customer c where c.active=0)

Kueri 2 ( rencana )

select * from dt where exists (select 1 from customer c where c.code=dt.customer and c.active=0)
George Menoutis
sumber
Sudahkah Anda mengujinya? Jika demikian, dapatkah Anda membagikan SQL dan hasil Anda?
UnhandledExcepSean
Mengujinya beberapa kali. Saya dapat membuat kasus uji lain, dan saya akan melakukannya, tetapi kasus uji tidak berarti bahwa pengoptimal akan melakukan rencana yang sama persis pada tabel dengan statistik berbeda. Ini mungkin membuat seseorang berpikir jawabannya parsial - tetapi tidak adanya beberapa operator semijoin adalah fakta. Mungkin saya akan menemukan daftar di suatu tempat dan menautkannya.
George Menoutis
5

Saya akan memilih EXISTS daripada IN, lihat tautan di bawah ini:

SQL Server: JOIN vs IN vs EXISTS - perbedaan logis

Ada kesalahpahaman umum bahwa IN berperilaku sama dengan EXISTS atau JOIN dalam hal hasil yang dikembalikan. Ini tidak benar.

IN: Mengembalikan true jika nilai yang ditentukan cocok dengan nilai apa pun di subkueri atau daftar.

Ada: Mengembalikan nilai true jika subkueri berisi baris apa pun.

Bergabung: Menggabungkan 2 resultets pada kolom bergabung.

Kredit blog: https://stackoverflow.com/users/31345/mladen-prajdic

Penyamak
sumber
Wah, terima kasih atas blog dan penjelasannya.
Christian Müller
3

Rencana eksekusi biasanya akan identik dalam kasus ini, tetapi sampai Anda melihat bagaimana faktor pengoptimal dalam semua aspek indeks lainnya, dll., Anda benar-benar tidak akan pernah tahu.

Cade Roux
sumber
3

Jadi, IN tidak sama dengan EXISTS dan juga tidak akan menghasilkan rencana eksekusi yang sama.

Biasanya EXISTS digunakan dalam subkueri berkorelasi, itu berarti Anda akan BERGABUNG dengan kueri dalam yang ada dengan kueri luar Anda. Itu akan menambahkan lebih banyak langkah untuk menghasilkan hasil saat Anda perlu menyelesaikan kueri luar bergabung dan kueri dalam bergabung lalu mencocokkan klausa tempat mereka untuk menggabungkan keduanya.

Biasanya IN digunakan tanpa menghubungkan kueri dalam dengan kueri luar, dan itu bisa diselesaikan hanya dalam satu langkah (dalam skenario kasus terbaik).

Pertimbangkan ini:

  1. Jika Anda menggunakan IN dan hasil query dalam adalah jutaan baris nilai yang berbeda, itu mungkin akan melakukan lebih lambat dari EXISTS mengingat bahwa query EXISTS adalah performant (memiliki indeks yang tepat untuk digabungkan dengan query luar).

  2. Jika Anda menggunakan EXISTS dan penggabungan dengan kueri luar Anda rumit (membutuhkan lebih banyak waktu untuk melakukan, tidak ada indeks yang sesuai) itu akan memperlambat kueri dengan jumlah baris di tabel luar, terkadang perkiraan waktu untuk menyelesaikan bisa dalam beberapa hari. Jika jumlah baris dapat diterima untuk perangkat keras yang Anda berikan, atau kardinalitas datanya benar (misalnya nilai DISTINCT yang lebih sedikit dalam kumpulan data yang besar) IN dapat bekerja lebih cepat daripada EXISTS.

  3. Semua hal di atas akan dicatat ketika Anda memiliki cukup banyak baris pada setiap tabel (maksud saya adalah sesuatu yang melebihi pemrosesan CPU Anda dan / atau ambang ram untuk caching).

Jadi JAWABANNYA TERGANTUNG. Anda dapat menulis kueri kompleks di dalam IN atau EXISTS, tetapi sebagai aturan praktis, Anda harus mencoba menggunakan IN dengan sekumpulan nilai berbeda dan EXISTS terbatas saat Anda memiliki banyak baris dengan banyak nilai berbeda.

Triknya adalah dengan membatasi jumlah baris yang akan dipindai.

Salam,

MarianoC

MarianoC
sumber
1

Untuk mengoptimalkan EXISTS , jadilah sangat literal; sesuatu harus ada di sana, tetapi Anda tidak benar-benar membutuhkan data apa pun yang dikembalikan dari sub-kueri terkait. Anda baru saja mengevaluasi kondisi Boolean.

Begitu:

WHERE EXISTS (SELECT TOP 1 1 FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

Karena sub-kueri terkait adalah RBAR, klik hasil pertama membuat kondisi menjadi benar, dan tidak diproses lebih lanjut.

Josh Lewis
sumber
Saya selalu sangat berhati-hati dalam menggunakan pengkodean LEFT JOIN + NULL, karena sangat mudah untuk mendapatkan hasil yang terlewat atau miring jika Anda tidak terlalu berhati-hati dalam penanganan NULL Anda. Saya sangat jarang menemukan situasi di mana EXISTS atau CTE (untuk menemukan duplikasi, atau penyisipan sintetis untuk data yang hilang), tidak memenuhi persyaratan yang sama dan mengungguli LEFT JOIN + NULL
Josh Lewis
3
TOP 1 harus benar-benar tidak relevan (atau acara redundan) saat digunakan dengan EXISTS. EXISTS selalu kembali segera setelah menemukan baris yang cocok.
Karl Kieninger
Sejauh ini saya tidak melihat manfaat kinerja apa pun dengan pendekatan ini. Tolong tunjukkan beberapa screenshot dari Rencana Eksekusi
DaFi4
-1

Tidak masuk akal dan tidak dijamin benar: Saya yakin yang kedua akan lebih cepat dalam kasus ini.

  1. Pertama, subkueri yang berkorelasi kemungkinan akan menyebabkan subkueri dijalankan untuk setiap baris.
  2. Pada contoh kedua, subkueri hanya boleh berjalan sekali, karena tidak berkorelasi.
  3. Dalam contoh kedua, INwasiat akan mengalami korsleting segera setelah menemukan kecocokan.
RedFilter
sumber