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


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.


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.


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


7

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


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.


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.


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.


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.


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.


4

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


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.


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.


2

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


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.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.