Apakah ada perbedaan eksekusi antara kondisi JOIN dan kondisi WHERE?

17

Apakah ada perbedaan kinerja antara dua contoh kueri ini?

Pertanyaan 1:

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
where  b.tag = 'Y'

Pertanyaan 2;

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
   and b.tag = 'Y'

Perhatikan satu-satunya perbedaan adalah penempatan kondisi tambahan; yang pertama menggunakan WHEREklausa dan yang kedua menambahkan kondisi ke ONklausa.

Ketika saya menjalankan kueri ini pada sistem Teradata saya, rencana jelaskan identik dan langkah GABUNG menunjukkan kondisi tambahan dalam setiap kasus. Namun, pada pertanyaan SO mengenai MySQL ini, salah satu jawaban menyarankan bahwa gaya kedua lebih disukai karena WHEREpemrosesan terjadi setelah gabungan dibuat.

Apakah ada aturan umum yang harus diikuti ketika pengkodean pertanyaan seperti ini? Saya kira itu harus tergantung platform karena jelas tidak membuat perbedaan pada database saya, tapi mungkin itu hanya fitur Teradata. Dan jika itu adalah tergantung platform, saya ingin sangat banyak untuk mendapatkan beberapa referensi dokumentasi; Saya benar-benar tidak tahu harus mencari apa.

BellevueBob
sumber
9
Ini bergantung pada platform, karena tergantung pada bagaimana pengoptimal RDBMS berurusan dengan penguraian dan pengoptimalan.
Philᵀᴹ
8
Dan jawaban itu dalam pertanyaan terkait layak mendapatkan beberapa downvotes. Bahkan pengoptimal primitif MySQL akan memahami bahwa kueri sederhana ini setara dan bahwa "klausa WHERE dievaluasi setelah semua penyatuan dibuat" benar hanya di level logis, bukan dalam eksekusi aktual.
ypercubeᵀᴹ
1
Bukan duplikat; pertanyaan itu dan jawabannya membandingkan sintaks GABUNG "implisit" versus "eksplisit". Saya bertanya secara khusus tentang persyaratan bergabung tambahan.
BellevueBob
Tidak akan berani memposting dalam jawaban seperti yang saya coba sebelumnya dan mendapat banyak suara. Ketika ada banyak bergabung, saya memiliki kasus pengalaman membawa kondisi ke dalam bergabung menghasilkan rencana permintaan yang lebih baik (itu disaring lebih awal). Masih hasilnya sama.
paparazzo

Jawaban:

14

Menurut Bab 9 (Parser dan Pengoptimal), Buku Memahami MySQL Internal oleh Sasha Pachev

Memahami MySQL Internal

di sini adalah rincian evaluasi permintaan sebagai tugas-tugas berikut:

  • Tentukan kunci mana yang dapat digunakan untuk mengambil catatan dari tabel, dan pilih yang terbaik untuk setiap tabel.
  • Untuk setiap tabel, putuskan apakah pemindaian tabel lebih baik daripada membaca pada kunci. Jika ada banyak catatan yang cocok dengan nilai kunci, keuntungan kunci berkurang dan pemindaian tabel menjadi lebih cepat.
  • Menentukan urutan tabel yang harus digabung ketika lebih dari satu tabel ada dalam kueri.
  • Tulis ulang klausa WHERE untuk menghilangkan kode mati, mengurangi perhitungan yang tidak perlu dan mengubah kendala sedapat mungkin untuk membuka jalan untuk menggunakan kunci.
  • Hilangkan tabel yang tidak digunakan dari bergabung.
  • Tentukan apakah kunci dapat digunakan untuk ORDER BYdan GROUP BY.
  • Mencoba menyederhanakan subqueries, serta menentukan sejauh mana hasil mereka dapat di-cache.
  • Gabungkan tampilan (perluas referensi tampilan sebagai makro)

Pada halaman yang sama, dikatakan sebagai berikut:

Dalam terminologi pengoptimal MySQL, setiap kueri adalah serangkaian gabungan. Istilah join digunakan di sini lebih luas daripada di perintah SQL. Kueri hanya pada satu tabel adalah gabungan yang menurun. Meskipun kami biasanya tidak menganggap membaca catatan dari satu tabel sebagai gabungan, struktur dan algoritma yang sama yang digunakan dengan gabungan konvensional berfungsi dengan baik untuk menyelesaikan kueri dengan hanya satu tabel.

EPILOG

Karena kunci yang ada, jumlah data, dan ekspresi kueri, MySQL Joins kadang-kadang dapat melakukan hal-hal untuk kebaikan kita sendiri (atau untuk membalas kita) dan menghasilkan hasil yang tidak kita harapkan dan tidak dapat dengan cepat dijelaskan.

Saya menulis tentang quirkiness ini sebelumnya

karena Pengoptimal Kueri MySQL dapat menghilangkan kunci tertentu selama evaluasi permintaan.

Komentar @ Phil membantu saya melihat cara mengirim jawaban ini (+1 untuk komentar @ Phil)

Komentar @ ypercube (+1 untuk yang ini juga) adalah versi ringkas dari posting saya karena Pengoptimal Permintaan MySQL adalah primitif. Sayangnya, itu harus karena berurusan dengan mesin penyimpanan luar.

KESIMPULAN

Adapun pertanyaan Anda yang sebenarnya, Pengoptimal Kueri MySQL akan menentukan metrik kinerja setiap permintaan saat selesai

  • menghitung baris
  • memilih kunci
  • memijat set hasil intermiten
  • Oh ya, melakukan GABUNGAN yang sebenarnya

Anda mungkin harus memaksa urutan eksekusi dengan menulis ulang (refactoring) kueri

Ini Query pertama yang Anda berikan

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
where  b.tag = 'Y';

Coba tulis ulang untuk mengevaluasi WHERE dulu

select count(*)
from   table1 a
join   (select key_col from table2 where tag='Y') b
on     b.key_col=a.key_col;

Itu pasti akan mengubah rencana EXPLAIN. Itu bisa menghasilkan hasil yang lebih baik atau lebih buruk.

Saya pernah menjawab pertanyaan di StackOverflow di mana saya menerapkan teknik ini. EXPLAIN itu menghebohkan tetapi performanya dinamit. Ini hanya berfungsi karena memiliki indeks yang benar hadir dan penggunaan LIMIT dalam subquery .

Seperti harga saham, ketika datang ke Permintaan dan mencoba untuk mengungkapkannya, pembatasan berlaku, hasil dapat bervariasi, dan kinerja masa lalu tidak menunjukkan hasil di masa depan.

RolandoMySQLDBA
sumber
2
+1 untuk detail info khusus MySQL dan khususnya untuk menipu saya agar mempelajari perbedaan antara "Epilog" dan "Kesimpulan"!
BellevueBob
Dalam posting saya, Epilog adalah sub-kesimpulan.
RolandoMySQLDBA
6
@Rando: Anda dapat menambahkan Aftermath tentang peningkatan pengoptimalisasi dalam versi MariaDB (5.3 dan 5.5) terbaru dan dalam versi MySQL utama (5.6) yang baru dirilis. Yang mungkin membuat beberapa penulisan ulang tidak perlu.
ypercubeᵀᴹ
1

Untuk Oracle, karena mySQL memiliki deskripsi yang panjang, kami memiliki 2 cara tingkat tinggi untuk memanfaatkan pengoptimal.

Pertama adalah Optimalisasi Berbasis Aturan (atau RBO). Oracle memiliki 15 aturan set-in-stone yang masing-masing kueri diuraikannya mencoba untuk mengikuti dalam urutan yang ditetapkan. Jika tidak dapat menghasilkan kueri yang dioptimalkan dari aturan 1, itu akan bergerak maju ke aturan 2 dan seterusnya hingga mencapai aturan 15.

untuk info lebih lanjut: https://docs.oracle.com/cd/B10500_01/server.920/a96533/rbo.htm

Ini memengaruhi kernel Oracle RDBMS dari 11.1 dan di bawahnya yang belum dikonversi ke Cost Based Optimizer (alias CBO). Oracle 11.2 dan lebih tinggi memerlukan pengoptimal CBO, tetapi dapat memaksa ID Sql tertentu untuk mengoptimalkan dalam metode RBO lama jika pengguna menginginkannya.

CBO untuk Oracle 11.1+ alih-alih membuat beberapa rencana eksekusi untuk SQL ID yang sama dan menjalankan yang dengan biaya yang paling tidak diantisipasi secara keseluruhan. Ini memanfaatkan banyak logika dari RBO, tetapi menganalisis statistik tabel untuk membuat biaya rencana eksekusi dinamis untuk setiap operasi yang harus dilakukan DB untuk memberikan data kepada pengguna akhir. Melaksanakan pemindaian tabel penuh pada tabel yang sangat besar benar-benar mahal; menjalankan pemindaian tabel penuh di atas meja dengan 10 baris itu murah. Dalam RBO ini dianggap operasi yang sama.

untuk info lebih lanjut: https://oracle-base.com/articles/misc/cost-based-optimizer-and-database-statistics

Untuk contoh permintaan spesifik Anda: Oracle kemungkinan akan mem-parsing informasi untuk membuat rencana eksekusi yang berbeda dan dengan demikian satu akan secara teknis lebih baik daripada yang lain. Namun, ini bisa menjadi perbedaan minimal. Melihat itu, baik Oracle RBO dan CBO ingin meminta 1 lebih karena menjalankan pada gabungan dengan kondisi kurang dan kemudian menyaring kolom tertentu dari tabel sementara yang dibuat dari gabungan.

JB-Learner
sumber
1

Jika Anda memiliki dua pertanyaan dan Anda berpikir bahwa mereka setara maka yang berikut ini dapat terjadi:

  1. Kedua pertanyaan memiliki rencana eksekusi yang sama. Itu baik dan itulah yang kami harapkan. Mari berharap ini adalah rencana eksekusi optimal untuk kueri.
  2. ada rencana eksekusi yang berbeda. Kami memiliki dua sub-bagian di sini.

    2.1 Permintaan memiliki rencana pelaksanaan yang berbeda tetapi kedua rencana memiliki kinerja yang sama baiknya. Itu baik juga. Tidak perlu bahwa untuk permintaan setara, rencana yang sama harus dihasilkan. Tetapi kinerjanya harus sama. Dan sekali lagi kami berharap ini adalah yang terbaik.

    2.2 Permintaan memiliki rencana pelaksanaan yang berbeda dan satu rencana lebih baik daripada yang lain. Lagi-lagi kami memiliki sub-bagian:

    2.2.1 Paket berbeda karena kueri tidak setara. Jadi hati-hati periksa apakah mereka benar-benar setara. Dalam kasus Anda mereka benar-benar setara.

    2.2.2 Paket berbeda tetapi kueri sama. Ini berarti pengoptimal tidak cukup matang. Di dunia yang sempurna dengan pengoptimal yang sempurna ini seharusnya tidak terjadi. Jadi ya, itu tergantung platform dan Anda harus mempelajari dokumen spesifik platform untuk mencari tahu mengapa ini terjadi.

    2.2.3 Paket berbeda, kueri sama, perangkat lunak database memiliki bug.

keajaiban173
sumber