Subqueries vs joins

158

Saya refactored bagian lambat dari aplikasi yang kami warisi dari perusahaan lain untuk menggunakan gabungan dalam daripada subquery seperti:

WHERE id IN (SELECT id FROM ...)

Permintaan refactored berjalan sekitar 100x lebih cepat. (~ 50 detik hingga ~ 0,3) Saya mengharapkan peningkatan, tetapi adakah yang bisa menjelaskan mengapa itu sangat drastis? Kolom yang digunakan di mana klausa semuanya diindeks. Apakah SQL mengeksekusi query di mana klausa sekali per baris atau sesuatu?

Perbarui - Jelaskan hasil:

Perbedaannya ada di bagian kedua dari kueri "di mana id di ()" -

2   DEPENDENT SUBQUERY  submission_tags ref st_tag_id   st_tag_id   4   const   2966    Using where

vs 1 baris yang diindeks dengan gabungan:

    SIMPLE  s   eq_ref  PRIMARY PRIMARY 4   newsladder_production.st.submission_id  1   Using index
palmsey
sumber
4
Kemungkinan duplikat Bergabung dengan sub-kueri
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
2
Bukan duplikat. Pertanyaan ini secara khusus tentang perbedaan kinerja yang mencolok. Pertanyaan lainnya lebih umum, terbuka tentang pro dan kontra dari setiap pendekatan dan mengapa satu pendekatan tampaknya lebih populer.
Basil Bourque
@simhumileco Itu tidak ada peningkatan, tidak ada perbedaan, itu bertentangan dengan apa yang penulis tulis & jenis edit untuk gaya kode tidak pantas. Kapan saya harus mengedit kode?
philipxy
Hai @ philipxy, saya tidak bermaksud ikut campur dalam pemikiran penulis, tetapi hanya untuk membuat fragmen kode lebih mudah dibaca dan ditulis lebih hati-hati.
simhumileco

Jawaban:

160

"Subquery berkorelasi" (yaitu, di mana kondisi tergantung pada nilai yang diperoleh dari baris kueri yang berisi) akan mengeksekusi satu kali untuk setiap baris. Subquery yang tidak berkorelasi (yang di mana kondisi independen dari kueri yang berisi) akan dieksekusi sekali di awal. Mesin SQL membuat perbedaan ini secara otomatis.

Tapi, ya, jelaskan-rencana akan memberi Anda rincian kotor.

Jeffrey L Whitledge
sumber
3
Harap perhatikan bahwa DEPENDENT SUBQUERYartinya sama persis dengan "subquery yang dikorelasikan".
Timo
38

Anda menjalankan subquery satu kali untuk setiap baris sedangkan gabung terjadi pada indeks.

Sklivvz
sumber
5
Saya pikir ini tidak benar. Mesin SQL harus menjalankan subquery hanya sekali dan menggunakan hasilnya sebagai daftar.
dacracot
8
Itu tergantung - jika subquery berkorelasi dengan kueri luar (menggunakan datanya), dieksekusi dengan setiap baris.
qbeuek
4
Mungkin benar dalam hal ini, tetapi secara umum tidak benar.
Amy B
1
OP's EXPLAINmengatakan DEPENDENT SUBQUERY, yang merupakan indikator paling jelas dari perilaku ini.
Timo
16

Berikut adalah contoh bagaimana sub-query dievaluasi dalam MySQL 6.0 .

Pengoptimal baru akan mengonversi subqueries semacam ini menjadi gabungan.

Giuseppe Maxia
sumber
Itu sebuah artikel besar pada MySQL 6.0 ditingkatkan optimizer, berkat
Api Gagak
7

Jalankan menjelaskan-rencana pada setiap versi, itu akan memberi tahu Anda alasannya.

scotta
sumber
6

sebelum kueri dijalankan terhadap dataset yang dimasukkan melalui pengoptimal kueri, pengoptimal berupaya mengatur kueri sedemikian rupa sehingga dapat menghapus sebanyak tupel (baris) dari hasil yang ditetapkan secepat mungkin. Seringkali ketika Anda menggunakan subquery (terutama yang buruk) tupel tidak dapat dipangkas dari hasil yang ditetapkan sampai kueri luar mulai berjalan.

Tanpa melihat kueri, sulit untuk mengatakan apa yang begitu buruk tentang aslinya, tapi tebakan saya adalah sesuatu yang pengoptimal tidak bisa membuat lebih baik. Menjalankan 'jelaskan' akan menunjukkan kepada Anda metode pengoptimal untuk mengambil data.

pfranza
sumber
4

Lihatlah paket permintaan untuk setiap permintaan.

Dimana dalam dan Bergabung dapat biasanya diimplementasikan dengan menggunakan rencana eksekusi yang sama, sehingga biasanya ada nol kecepatan-up dari mengubah antara mereka.

Amy B
sumber
3
Haha, saya <3 Sql scrub yang turun karena mereka tidak tahu cara membaca rencana permintaan.
Amy B
4

Pengoptimal tidak melakukan pekerjaan dengan sangat baik. Biasanya mereka dapat diubah tanpa perbedaan dan pengoptimal dapat melakukan ini.

Cade Roux
sumber
4

Biasanya ini adalah hasil dari pengoptimal yang tidak dapat mengetahui bahwa subquery dapat dieksekusi sebagai gabungan dalam hal ini mengeksekusi subquery untuk setiap catatan dalam tabel daripada bergabung dengan tabel dalam subquery terhadap tabel yang Anda query. Beberapa basis data yang lebih "enterprisey" lebih baik dalam hal ini, tetapi terkadang mereka masih melewatkannya.

Mark Roddy
sumber
4

Pertanyaan ini agak umum, jadi inilah jawaban umum:

Pada dasarnya, permintaan memakan waktu lebih lama ketika MySQL memiliki banyak baris untuk disortir.

Melakukan hal ini:

Jalankan EXPLAIN di masing-masing kueri (yang GABUNG, lalu yang Subqueried), dan poskan hasilnya di sini.

Saya pikir melihat perbedaan dalam interpretasi MySQL dari pertanyaan-pertanyaan itu akan menjadi pengalaman belajar bagi semua orang.

Pete Karl II
sumber
4

Subquery mana harus menjalankan 1 kueri untuk setiap baris yang dikembalikan. Bergabung dalam hanya harus menjalankan 1 permintaan.

Shawn
sumber
3

Subquery mungkin menjalankan "pemindaian tabel penuh". Dengan kata lain, tidak menggunakan indeks dan mengembalikan terlalu banyak baris yang harus disaring dari mana dari kueri utama.

Dugaan saja tanpa perincian tentu saja tapi itulah situasi umum.

igelkott
sumber
2

Dengan subquery, Anda harus menjalankan kembali SELECT kedua untuk setiap hasil, dan setiap eksekusi biasanya mengembalikan 1 baris.

Dengan bergabung, SELECT kedua mengembalikan lebih banyak baris, tetapi Anda hanya perlu menjalankannya sekali. Keuntungannya adalah bahwa sekarang Anda dapat bergabung pada hasilnya, dan bergabung dengan relasi adalah apa yang seharusnya dimiliki oleh suatu database. Misalnya, mungkin pengoptimal dapat menemukan cara untuk memanfaatkan indeks dengan lebih baik sekarang.

Joel Coehoorn
sumber
2

Ini bukan subquery seperti klausa IN, meskipun bergabung pada fondasi dari setidaknya mesin SQL Oracle dan berjalan sangat cepat.

dacracot
sumber
1
di mana di benar-benar tidak secara inheren buruk.
Shawn
2

Diambil dari Referensi Manual ( 14.2.10.11 Subqueries Menulis Ulang sebagai Bergabung ):

SEBUAH KIRI [OUTER] BERGABUNG bisa lebih cepat daripada subquery yang setara karena server mungkin dapat mengoptimalkannya lebih baik — fakta yang tidak khusus untuk MySQL Server saja.

Jadi subqueries bisa lebih lambat dari LEFT [OUTER] BERGABUNG.

simhumileco
sumber