Pendekatan "everything is a nested-loop join" MySQL untuk eksekusi kueri tidak ideal untuk mengoptimalkan setiap jenis kueri. Untungnya, hanya ada beberapa kasus di mana pengoptimal kueri MySQL melakukan pekerjaan yang buruk, dan biasanya mungkin untuk menulis ulang kueri semacam itu dengan lebih efisien.
MySQL terkadang mengoptimalkan subkueri dengan sangat buruk. Pelanggar terburuk adalah
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];_9 subkueri dalam klausa
SELECT * FROM sakila.film WHERE0. Sebagai contoh, mari temukan semua film dalam tabelEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
SELECT * FROM sakila.film WHERE1 database sampel Sakila yang pemerannya termasuk aktris Penelope Guinness [EXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
SELECT * FROM sakila.film WHERE2]. Ini terasa alami untuk menulis dengan subquery, sebagai berikutEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
mysql>_SELECT * FROM sakila.film
->WHERE film_id IN[
->SELECT film_id FROM sakila.film_actor WHERE actor_id = 1];
Sangat menggoda untuk berpikir bahwa MySQL akan mengeksekusi kueri ini dari dalam ke luar, dengan menemukan daftar nilai
SELECT * FROM sakila.film WHERE3 dan menggantinya ke dalam daftarEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];9. Kami mengatakan daftar
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];_9 umumnya sangat cepat, jadi Anda mungkin mengharapkan kueri dioptimalkan untuk sesuatu seperti ini
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];
Sayangnya, justru sebaliknya yang terjadi. MySQL mencoba untuk "membantu" subquery dengan mendorong korelasi ke dalamnya dari tabel luar, yang menurutnya akan membuat subquery menemukan baris dengan lebih efisien. Itu menulis ulang kueri sebagai berikut
SELECT * FROM sakila.film WHERE_EXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
Sekarang subquery membutuhkan
SELECT * FROM sakila.film WHERE_6 dari tabelEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
SELECT * FROM sakila.film WHERE7 luar dan tidak dapat dijalankan terlebih dahulu.EXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
SELECT * FROM sakila.film WHERE_8 menunjukkan hasilnya sebagaiEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
SELECT * FROM sakila.film WHERE9 [Anda dapat menggunakanEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
mysql> EXPLAIN SELECT * FROM sakila.film ...;
+----+--------------------+------------+--------+------------------------+
| id | select_type | table | type | possible_keys |
+----+--------------------+------------+--------+------------------------+
| 1 | PRIMARY | film | ALL | NULL |
| 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id |
+----+--------------------+------------+--------+------------------------+
0 untuk melihat persis bagaimana kueri ditulis ulang]mysql> EXPLAIN SELECT * FROM sakila.film ...;
+----+--------------------+------------+--------+------------------------+
| id | select_type | table | type | possible_keys |
+----+--------------------+------------+--------+------------------------+
| 1 | PRIMARY | film | ALL | NULL |
| 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id |
+----+--------------------+------------+--------+------------------------+
Menurut output
SELECT * FROM sakila.film WHERE_8, MySQL akan memindai tabelEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
SELECT * FROM sakila.film WHERE7 tabel dan mengeksekusi subquery untuk setiap baris yang ditemukannya. Ini tidak akan menyebabkan kinerja yang mencolok di meja kecil, tetapi jika meja luar sangat besar, kinerjanya akan sangat buruk. Untungnya, mudah untuk menulis ulang kueri sepertiEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
mysql> EXPLAIN SELECT * FROM sakila.film ...;
+----+--------------------+------------+--------+------------------------+
| id | select_type | table | type | possible_keys |
+----+--------------------+------------+--------+------------------------+
| 1 | PRIMARY | film | ALL | NULL |
| 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id |
+----+--------------------+------------+--------+------------------------+
3mysql>SELECT film.* FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id]
->WHERE actor_id = 1;
Pengoptimalan lain yang baik adalah membuat daftar
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];9 secara manual dengan menjalankan subquery sebagai kueri terpisah dengan
mysql> EXPLAIN SELECT * FROM sakila.film ...;
+----+--------------------+------------+--------+------------------------+
| id | select_type | table | type | possible_keys |
+----+--------------------+------------+--------+------------------------+
| 1 | PRIMARY | film | ALL | NULL |
| 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id |
+----+--------------------+------------+--------+------------------------+
5. Terkadang ini bisa lebih cepat daripada mysql> EXPLAIN SELECT * FROM sakila.film ...;
+----+--------------------+------------+--------+------------------------+
| id | select_type | table | type | possible_keys |
+----+--------------------+------------+--------+------------------------+
| 1 | PRIMARY | film | ALL | NULL |
| 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id |
+----+--------------------+------------+--------+------------------------+
3MySQL telah dikritik secara menyeluruh untuk jenis rencana eksekusi subquery khusus ini. Meski sudah pasti perlu diperbaiki, kritik tersebut seringkali membingungkan dua masalah yang berbeda. perintah eksekusi dan caching. Menjalankan kueri dari dalam ke luar adalah salah satu cara untuk mengoptimalkannya; . Menulis ulang kueri sendiri memungkinkan Anda mengontrol kedua aspek tersebut. Versi MySQL yang akan datang harus dapat mengoptimalkan jenis kueri ini dengan lebih baik, meskipun ini bukanlah tugas yang mudah. Ada kasus terburuk yang sangat buruk untuk rencana eksekusi apa pun, termasuk rencana eksekusi luar-dalam yang menurut beberapa orang akan mudah dioptimalkan
Ketika subquery berkorelasi baik
MySQL tidak selalu mengoptimalkan subkueri berkorelasi dengan buruk. Jika Anda mendengar nasihat untuk selalu menghindarinya, jangan dengarkan. Sebaliknya, tolok ukur dan buat keputusan Anda sendiri. Terkadang subkueri yang dikorelasikan adalah cara yang masuk akal, atau bahkan optimal, untuk mendapatkan hasil. Mari kita lihat sebuah contoh
mysql>EXPLAIN SELECT film_id, language_id FROM sakila.film
->WHERE NOT EXISTS[
->SELECT * FROM sakila.film_actor
->WHERE film_actor.film_id = film.film_id
->]\G
*************************** 1. row *************************** id: 1 select_type: PRIMARY table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: Using where *************************** 2. row *************************** id: 2 select_type: DEPENDENT SUBQUERY table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: film.film_id rows: 2 Extra: Using where; Using index
Saran standar untuk kueri ini adalah menuliskannya sebagai
mysql> EXPLAIN SELECT * FROM sakila.film ...;
+----+--------------------+------------+--------+------------------------+
| id | select_type | table | type | possible_keys |
+----+--------------------+------------+--------+------------------------+
| 1 | PRIMARY | film | ALL | NULL |
| 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id |
+----+--------------------+------------+--------+------------------------+
7 alih-alih menggunakan subkueri. Secara teori, rencana eksekusi MySQL pada dasarnya akan sama. Mari kita lihatmysql>EXPLAIN SELECT film.film_id, film.language_id
->FROM sakila.film
->LEFT OUTER JOIN sakila.film_actor USING[film_id]
->WHERE film_actor.film_id IS NULL\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: sakila.film.film_id rows: 2 Extra: Using where; Using index;Not exists
Rencananya hampir identik, tetapi ada beberapa perbedaan
Jenis
mysql>
_8 terhadapEXPLAIN SELECT * FROM sakila.film ...;
+----+--------------------+------------+--------+------------------------+ | id | select_type | table | type | possible_keys | +----+--------------------+------------+--------+------------------------+ | 1 | PRIMARY | film | ALL | NULL | | 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id | +----+--------------------+------------+--------+------------------------+mysql>
9 adalahEXPLAIN SELECT * FROM sakila.film ...;
+----+--------------------+------------+--------+------------------------+ | id | select_type | table | type | possible_keys | +----+--------------------+------------+--------+------------------------+ | 1 | PRIMARY | film | ALL | NULL | | 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id | +----+--------------------+------------+--------+------------------------+SELECT * FROM sakila.film WHERE
9 dalam satu kueri danEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
mysql>
1 dalam kueri lainnya. Perbedaan ini hanya mencerminkan sintaks, karena kueri pertama menggunakan subkueri dan yang kedua tidak. Itu tidak membuat banyak perbedaan dalam hal operasi penanganSELECT film.* FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id]
->WHERE actor_id = 1;
Permintaan kedua tidak mengatakan "Menggunakan di mana" di kolom
mysql>
2 untuk tabelSELECT film.* FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id]
->WHERE actor_id = 1;
SELECT * FROM sakila.film WHERE
7. Tapi itu tidak masalah. klausaEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
mysql>
_4 kueri kedua sama saja dengan klausaSELECT film.* FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id]
->WHERE actor_id = 1;
SELECT * FROM sakila.film WHERE
0EXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
Permintaan kedua mengatakan "Tidak ada" di kolom
mysql>
9 tabelEXPLAIN SELECT * FROM sakila.film ...;
+----+--------------------+------------+--------+------------------------+ | id | select_type | table | type | possible_keys | +----+--------------------+------------+--------+------------------------+ | 1 | PRIMARY | film | ALL | NULL | | 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id | +----+--------------------+------------+--------+------------------------+mysql>
2. Ini adalah contoh dari algoritma terminasi dini yang kami sebutkan sebelumnya di bab ini. Itu berarti MySQL menggunakan pengoptimalan yang tidak ada untuk menghindari membaca lebih dari satu baris dalam indeksSELECT film.* FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id]
->WHERE actor_id = 1;
mysql>
9 tabelEXPLAIN SELECT * FROM sakila.film ...;
+----+--------------------+------------+--------+------------------------+ | id | select_type | table | type | possible_keys | +----+--------------------+------------+--------+------------------------+ | 1 | PRIMARY | film | ALL | NULL | | 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id | +----+--------------------+------------+--------+------------------------+mysql>
9. Ini setara denganSELECT film.* FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id]
->WHERE actor_id = 1;
mysql>
0EXPLAIN SELECT film_id, language_id FROM sakila.film
->WHERE NOT EXISTS[
->SELECT * FROM sakila.film_actor
->WHERE film_actor.film_id = film.film_id
->]\G
*************************** 1. row *************************** id: 1 select_type: PRIMARY table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: Using where *************************** 2. row *************************** id: 2 select_type: DEPENDENT SUBQUERY table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: film.film_id rows: 2 Extra: Using where; Using indexmysql>
1 subquery berkorelasi, karena berhenti memproses baris saat ini segera setelah menemukan kecocokanEXPLAIN SELECT film_id, language_id FROM sakila.film
->WHERE NOT EXISTS[
->SELECT * FROM sakila.film_actor
->WHERE film_actor.film_id = film.film_id
->]\G
*************************** 1. row *************************** id: 1 select_type: PRIMARY table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: Using where *************************** 2. row *************************** id: 2 select_type: DEPENDENT SUBQUERY table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: film.film_id rows: 2 Extra: Using where; Using index
Jadi, secara teori, MySQL akan mengeksekusi kueri hampir sama. Pada kenyataannya, pembandingan adalah satu-satunya cara untuk mengetahui pendekatan mana yang benar-benar lebih cepat. Kami membandingkan kedua kueri pada penyiapan standar kami. Hasilnya ditampilkan di
Tabel 4-1. NOT EXISTS versus LEFT OUTER JOIN
Pertanyaan
Menghasilkan kueri per detik [QPS]
mysql>_2 subqueryEXPLAIN SELECT film_id, language_id FROM sakila.film
->WHERE NOT EXISTS[
->SELECT * FROM sakila.film_actor
->WHERE film_actor.film_id = film.film_id
->]\G
*************************** 1. row *************************** id: 1 select_type: PRIMARY table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: Using where *************************** 2. row *************************** id: 2 select_type: DEPENDENT SUBQUERY table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: film.film_id rows: 2 Extra: Using where; Using index
360 QPS
mysql> EXPLAIN SELECT * FROM sakila.film ...;
+----+--------------------+------------+--------+------------------------+
| id | select_type | table | type | possible_keys |
+----+--------------------+------------+--------+------------------------+
| 1 | PRIMARY | film | ALL | NULL |
| 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id |
+----+--------------------+------------+--------+------------------------+
_7425 QPS
Tolok ukur kami menemukan bahwa subquery sedikit lebih lambat
Namun, ini tidak selalu terjadi. Terkadang subquery bisa lebih cepat. Misalnya, ini bisa bekerja dengan baik saat Anda hanya ingin melihat baris dari satu tabel yang cocok dengan baris di tabel lain. Meskipun kedengarannya seperti menggambarkan gabungan dengan sempurna, itu tidak selalu sama. Penggabungan berikut, yang dirancang untuk menemukan setiap film yang memiliki aktor, akan mengembalikan duplikat karena beberapa film memiliki banyak aktor
mysql>SELECT film.film_id FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id];
Kita perlu menggunakan
mysql>_4 atauEXPLAIN SELECT film_id, language_id FROM sakila.film
->WHERE NOT EXISTS[
->SELECT * FROM sakila.film_actor
->WHERE film_actor.film_id = film.film_id
->]\G
*************************** 1. row *************************** id: 1 select_type: PRIMARY table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: Using where *************************** 2. row *************************** id: 2 select_type: DEPENDENT SUBQUERY table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: film.film_id rows: 2 Extra: Using where; Using index
mysql>5 untuk menghilangkan duplikatEXPLAIN SELECT film_id, language_id FROM sakila.film
->WHERE NOT EXISTS[
->SELECT * FROM sakila.film_actor
->WHERE film_actor.film_id = film.film_id
->]\G
*************************** 1. row *************************** id: 1 select_type: PRIMARY table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: Using where *************************** 2. row *************************** id: 2 select_type: DEPENDENT SUBQUERY table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: film.film_id rows: 2 Extra: Using where; Using index
mysql>SELECT DISTINCT film.film_id FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id];
Tapi apa yang sebenarnya kami coba ungkapkan dengan kueri ini, dan apakah itu jelas dari SQL? . Inilah kueri yang ditulis sebagai subkueri, bukan gabungan
mysql>SELECT film_id FROM sakila.film
->WHERE EXISTS[SELECT * FROM sakila.film_actor
->WHERE film.film_id = film_actor.film_id];
Sekali lagi, kami membandingkan untuk melihat strategi mana yang lebih cepat. Hasilnya ditampilkan di
Tabel 4-2. ADA vs GABUNG DALAM
Pertanyaan
Menghasilkan kueri per detik [QPS]
mysql>_9EXPLAIN SELECT film_id, language_id FROM sakila.film
->WHERE NOT EXISTS[
->SELECT * FROM sakila.film_actor
->WHERE film_actor.film_id = film.film_id
->]\G
*************************** 1. row *************************** id: 1 select_type: PRIMARY table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: Using where *************************** 2. row *************************** id: 2 select_type: DEPENDENT SUBQUERY table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: film.film_id rows: 2 Extra: Using where; Using index
185 KPS
mysql>_6 subqueryEXPLAIN SELECT film_id, language_id FROM sakila.film
->WHERE NOT EXISTS[
->SELECT * FROM sakila.film_actor
->WHERE film_actor.film_id = film.film_id
->]\G
*************************** 1. row *************************** id: 1 select_type: PRIMARY table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: Using where *************************** 2. row *************************** id: 2 select_type: DEPENDENT SUBQUERY table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: film.film_id rows: 2 Extra: Using where; Using index
325 QPS
Dalam contoh ini, subquery bekerja lebih cepat daripada join
Kami menunjukkan contoh panjang ini untuk mengilustrasikan dua poin. Anda tidak boleh mengindahkan saran kategoris tentang subkueri, dan Anda harus menggunakan tolok ukur untuk membuktikan asumsi Anda tentang rencana kueri dan kecepatan eksekusi
MySQL terkadang tidak dapat "menekan" kondisi dari luar
mysql>1 ke dalam, di mana kondisi tersebut dapat digunakan untuk membatasi hasil atau mengaktifkan pengoptimalan tambahanEXPLAIN SELECT film.film_id, film.language_id
->FROM sakila.film
->LEFT OUTER JOIN sakila.film_actor USING[film_id]
->WHERE film_actor.film_id IS NULL\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: sakila.film.film_id rows: 2 Extra: Using where; Using index;Not exists
Jika menurut Anda salah satu kueri individual di dalam
mysql>1 akan mendapat manfaat dariEXPLAIN SELECT film.film_id, film.language_id
->FROM sakila.film
->LEFT OUTER JOIN sakila.film_actor USING[film_id]
->WHERE film_actor.film_id IS NULL\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: sakila.film.film_id rows: 2 Extra: Using where; Using index;Not exists
mysql>3, atau jika Anda tahu bahwa kueri tersebut akan tunduk pada klausaEXPLAIN SELECT film.film_id, film.language_id
->FROM sakila.film
->LEFT OUTER JOIN sakila.film_actor USING[film_id]
->WHERE film_actor.film_id IS NULL\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: sakila.film.film_id rows: 2 Extra: Using where; Using index;Not exists
mysql>4 setelah digabungkan dengan kueri lain, Anda perlu memasukkan klausa tersebut di dalam setiap bagianEXPLAIN SELECT film.film_id, film.language_id
->FROM sakila.film
->LEFT OUTER JOIN sakila.film_actor USING[film_id]
->WHERE film_actor.film_id IS NULL\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: sakila.film.film_id rows: 2 Extra: Using where; Using index;Not exists
mysql>1. Misalnya, jika AndaEXPLAIN SELECT film.film_id, film.language_id
->FROM sakila.film
->LEFT OUTER JOIN sakila.film_actor USING[film_id]
->WHERE film_actor.film_id IS NULL\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: sakila.film.film_id rows: 2 Extra: Using where; Using index;Not exists
mysql>_1 bersama dua tabel besar danEXPLAIN SELECT film.film_id, film.language_id
->FROM sakila.film
->LEFT OUTER JOIN sakila.film_actor USING[film_id]
->WHERE film_actor.film_id IS NULL\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: sakila.film.film_id rows: 2 Extra: Using where; Using index;Not exists
mysql>3 hasil ke 20 baris pertama, MySQL akan menyimpan kedua tabel besar ke tabel sementara dan kemudian mengambil hanya 20 baris darinya. Anda dapat menghindari ini dengan menempatkanEXPLAIN SELECT film.film_id, film.language_id
->FROM sakila.film
->LEFT OUTER JOIN sakila.film_actor USING[film_id]
->WHERE film_actor.film_id IS NULL\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: sakila.film.film_id rows: 2 Extra: Using where; Using index;Not exists
mysql>_8 pada setiap kueri di dalamEXPLAIN SELECT film.film_id, film.language_id
->FROM sakila.film
->LEFT OUTER JOIN sakila.film_actor USING[film_id]
->WHERE film_actor.film_id IS NULL\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: sakila.film.film_id rows: 2 Extra: Using where; Using index;Not exists
mysql>1EXPLAIN SELECT film.film_id, film.language_id
->FROM sakila.film
->LEFT OUTER JOIN sakila.film_actor USING[film_id]
->WHERE film_actor.film_id IS NULL\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: sakila.film.film_id rows: 2 Extra: Using where; Using index;Not exists
Pengoptimalan penggabungan indeks
Algoritma penggabungan indeks, diperkenalkan di MySQL 5. 0, biarkan MySQL menggunakan lebih dari satu indeks per tabel dalam kueri. Versi MySQL sebelumnya hanya dapat menggunakan indeks tunggal, jadi ketika tidak ada indeks tunggal yang cukup baik untuk membantu semua batasan dalam klausa
SELECT * FROM sakila.film WHERE0, MySQL sering memilih pemindaian tabel. Misalnya, tabelEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
mysql> EXPLAIN SELECT * FROM sakila.film ...;
+----+--------------------+------------+--------+------------------------+
| id | select_type | table | type | possible_keys |
+----+--------------------+------------+--------+------------------------+
| 1 | PRIMARY | film | ALL | NULL |
| 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id |
+----+--------------------+------------+--------+------------------------+
_9 memiliki indeks pada SELECT * FROM sakila.film WHERE6 dan indeks padaEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
SELECT * FROM sakila.film WHERE3, tetapi keduanya bukanlah pilihan yang baik untuk kedua kondisiEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
SELECT * FROM sakila.film WHERE0 dalam kueri iniEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];0
Di versi MySQL yang lebih lama, kueri tersebut akan menghasilkan pemindaian tabel kecuali jika Anda menulisnya sebagai
mysql>1 dari dua kueriEXPLAIN SELECT film.film_id, film.language_id
->FROM sakila.film
->LEFT OUTER JOIN sakila.film_actor USING[film_id]
->WHERE film_actor.film_id IS NULL\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: sakila.film.film_id rows: 2 Extra: Using where; Using index;Not exists
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];_1
Di MySQL5. 0 dan yang lebih baru, bagaimanapun, kueri dapat menggunakan kedua indeks, memindainya secara bersamaan dan menggabungkan hasilnya. Ada tiga variasi pada algoritma. penyatuan untuk
mysql>6 kondisi, persimpangan untukSELECT film.film_id FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id];
mysql>7 kondisi, dan penyatuan persimpangan untuk kombinasi keduanya. Kueri berikut menggunakan gabungan dari dua pemindaian indeks, seperti yang Anda lihat dengan memeriksa kolomSELECT film.film_id FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id];
mysql>2SELECT film.* FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id]
->WHERE actor_id = 1;
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];_2
MySQL dapat menggunakan teknik ini pada klausa
SELECT * FROM sakila.film WHERE_0 yang kompleks, sehingga Anda dapat melihat operasi bersarang di kolomEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
mysql>2 untuk beberapa kueri. Ini sering bekerja dengan sangat baik, tetapi terkadang operasi penyanggaan, penyortiran, dan penggabungan algoritme menggunakan banyak sumber daya CPU dan memori. Ini terutama benar jika tidak semua indeks sangat selektif, sehingga pemindaian paralel mengembalikan banyak baris ke operasi penggabungan. Ingatlah bahwa pengoptimal tidak memperhitungkan biaya ini—pengoptimal hanya mengoptimalkan jumlah halaman acak yang dibaca. Hal ini dapat membuatnya "di bawah harga" kueri, yang sebenarnya mungkin berjalan lebih lambat daripada pemindaian tabel biasa. Penggunaan memori dan CPU yang intensif juga cenderung memengaruhi kueri bersamaan, tetapi Anda tidak akan melihat efek ini saat menjalankan kueri secara terpisah. Ini adalah alasan lain untuk merancang tolok ukur yang realistisSELECT film.* FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id]
->WHERE actor_id = 1;
Jika kueri Anda berjalan lebih lambat karena batasan pengoptimal ini, Anda dapat mengatasinya dengan menonaktifkan beberapa indeks dengan
mysql>1, atau kembali ke taktik lamaSELECT DISTINCT film.film_id FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id];
mysql>1EXPLAIN SELECT film.film_id, film.language_id
->FROM sakila.film
->LEFT OUTER JOIN sakila.film_actor USING[film_id]
->WHERE film_actor.film_id IS NULL\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: sakila.film.film_id rows: 2 Extra: Using where; Using index;Not exists
Penyebaran kesetaraan terkadang memiliki biaya yang tidak terduga. Misalnya, pertimbangkan daftar
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];_9 besar pada kolom yang diketahui pengoptimal akan sama dengan beberapa kolom pada tabel lain, karena
mysql>4, atauSELECT DISTINCT film.film_id FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id];
mysql>4 klausa yang menyetel kolom sama satu sama lainSELECT film.* FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id]
->WHERE actor_id = 1;
Pengoptimal akan "berbagi" daftar dengan menyalinnya ke kolom terkait di semua tabel terkait. Ini biasanya membantu, karena memberikan pengoptimal kueri dan mesin eksekusi lebih banyak opsi untuk tempat mengeksekusi pemeriksaan
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];9. Namun jika daftarnya sangat besar, ini dapat mengakibatkan pengoptimalan dan eksekusi yang lebih lambat. Tidak ada solusi bawaan untuk masalah ini pada saat penulisan ini—Anda harus mengubah kode sumber jika itu masalah bagi Anda. [Ini bukan masalah bagi kebanyakan orang. ]
MySQL tidak dapat menjalankan satu kueri secara paralel di banyak CPU. Ini adalah fitur yang ditawarkan oleh beberapa server database lain, tetapi bukan MySQL. Kami menyebutkannya agar Anda tidak menghabiskan banyak waktu untuk mencari tahu cara mendapatkan eksekusi kueri paralel di MySQL
MySQL tidak dapat melakukan gabungan hash yang sebenarnya pada saat penulisan ini—semuanya adalah gabungan loop bersarang. Namun, Anda dapat meniru gabungan hash menggunakan indeks hash. Jika Anda tidak menggunakan mesin penyimpanan Memori, Anda juga harus meniru indeks hash. Kami menunjukkan kepada Anda bagaimana melakukan ini di "Membangun indeks hash Anda sendiri".
MySQL secara historis tidak dapat melakukan pemindaian indeks longgar, yang memindai rentang indeks yang tidak berdekatan. Pemindaian indeks MySQL umumnya memerlukan titik awal yang ditentukan dan titik akhir yang ditentukan dalam indeks, bahkan jika hanya beberapa baris yang tidak bersebelahan di tengah yang benar-benar diinginkan untuk kueri. MySQL akan memindai seluruh rentang baris dalam titik akhir ini
Sebuah contoh akan membantu memperjelas hal ini. Misalkan kita memiliki tabel dengan indeks pada kolom
mysql>7 dan kita ingin menjalankan kueri berikutSELECT DISTINCT film.film_id FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id];
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];_3
Karena indeks dimulai dengan kolom
mysql>_8, tetapi klausaSELECT DISTINCT film.film_id FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id];
SELECT * FROM sakila.film WHERE0 kueri tidak menentukan kolomEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
mysql>8, MySQL akan melakukan pemindaian tabel dan menghilangkan baris yang tidak cocok dengan klausaSELECT DISTINCT film.film_id FROM sakila.film
->INNER JOIN sakila.film_actor USING[film_id];
SELECT * FROM sakila.film WHERE0, seperti yang ditunjukkan padaEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
Gambar 4-5. MySQL memindai seluruh tabel untuk menemukan baris
Sangat mudah untuk melihat bahwa ada cara yang lebih cepat untuk menjalankan kueri ini. Struktur indeks [tetapi bukan API mesin penyimpanan MySQL] memungkinkan Anda mencari ke awal setiap rentang nilai, memindai hingga akhir rentang, lalu mundur dan melompat ke awal rentang berikutnya. menunjukkan seperti apa strategi itu jika MySQL mampu melakukannya
Perhatikan tidak adanya
SELECT * FROM sakila.film WHERE0 klausa, yang tidak diperlukan karena indeks saja memungkinkan kita melewati baris yang tidak diinginkan. [Sekali lagi, MySQL belum bisa melakukan ini. ]EXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
Gambar 4-6. Pemindaian indeks longgar, yang saat ini tidak dapat dilakukan MySQL, akan lebih efisien
Memang ini adalah contoh sederhana, dan kami dapat dengan mudah mengoptimalkan kueri yang telah kami tampilkan dengan menambahkan indeks yang berbeda. Namun, ada banyak kasus di mana menambahkan indeks lain tidak dapat menyelesaikan masalah. Salah satu contohnya adalah kueri yang memiliki kondisi jangkauan di kolom pertama indeks dan kondisi persamaan di kolom kedua
Mulai dari MySQL 5. 0, pemindaian indeks longgar dimungkinkan dalam keadaan terbatas tertentu, seperti kueri yang menemukan nilai maksimum dan minimum dalam kueri yang dikelompokkan
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];_4
Informasi “Menggunakan indeks untuk kelompok menurut” dalam paket
SELECT * FROM sakila.film WHERE8 ini menunjukkan pemindaian indeks yang longgar. Ini adalah pengoptimalan yang bagus untuk tujuan khusus ini, tetapi ini bukan pemindaian indeks longgar untuk tujuan umum. Mungkin lebih baik disebut "probe indeks longgar. ”EXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
Hingga MySQL mendukung pemindaian indeks longgar tujuan umum, solusinya adalah menyediakan konstanta atau daftar konstanta untuk kolom utama indeks. Kami menunjukkan beberapa contoh cara mendapatkan kinerja yang baik dengan jenis kueri ini dalam studi kasus pengindeksan kami di bab sebelumnya
MySQL tidak mengoptimalkan kueri
mysql>4 danSELECT film_id FROM sakila.film
->WHERE EXISTS[SELECT * FROM sakila.film_actor
->WHERE film.film_id = film_actor.film_id];
mysql>5 tertentu dengan sangat baik. Ini sebuah contohSELECT film_id FROM sakila.film
->WHERE EXISTS[SELECT * FROM sakila.film_actor
->WHERE film.film_id = film_actor.film_id];
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];5
Karena tidak ada indeks pada
mysql>6, kueri ini melakukan pemindaian tabel. Jika MySQL memindai kunci utama, secara teoritis dapat berhenti setelah membaca baris pertama yang cocok, karena kunci utama benar-benar menaik dan setiap baris berikutnya akan memilikiSELECT film_id FROM sakila.film
->WHERE EXISTS[SELECT * FROM sakila.film_actor
->WHERE film.film_id = film_actor.film_id];
SELECT * FROM sakila.film WHERE3 yang lebih besar. Namun, dalam kasus ini, MySQL akan memindai seluruh tabel, yang dapat Anda verifikasi dengan membuat profil kueri. Solusinya adalah menghapusEXISTS
[ SELECT * FROM sakila.film_actor WHERE actor_id = 1AND film_actor.film_id = film.film_id];
mysql>_4 dan menulis ulang kueri denganSELECT film_id FROM sakila.film
->WHERE EXISTS[SELECT * FROM sakila.film_actor
->WHERE film.film_id = film_actor.film_id];
mysql>3, sebagai berikutEXPLAIN SELECT film.film_id, film.language_id
->FROM sakila.film
->LEFT OUTER JOIN sakila.film_actor USING[film_id]
->WHERE film_actor.film_id IS NULL\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: film type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: *************************** 2. row *************************** id: 1 select_type: SIMPLE table: film_actor type: ref possible_keys: idx_fk_film_id key: idx_fk_film_id key_len: 2 ref: sakila.film.film_id rows: 2 Extra: Using where; Using index;Not exists
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];_6
Strategi umum ini sering bekerja dengan baik ketika MySQL memilih untuk memindai lebih banyak baris daripada yang diperlukan. Jika Anda seorang yang murni, Anda mungkin keberatan bahwa kueri ini tidak memiliki inti dari SQL. Kami seharusnya dapat memberi tahu server apa yang kami inginkan dan server seharusnya mengetahui cara mendapatkan data itu, sedangkan, dalam hal ini, kami memberi tahu MySQL cara menjalankan kueri dan, akibatnya, tidak . Benar, tetapi terkadang Anda harus mengkompromikan prinsip Anda untuk mendapatkan kinerja yang tinggi
SELECT dan UPDATE di meja yang sama
MySQL tidak membiarkan Anda
mysql> EXPLAIN SELECT * FROM sakila.film ...;
+----+--------------------+------------+--------+------------------------+
| id | select_type | table | type | possible_keys |
+----+--------------------+------------+--------+------------------------+
| 1 | PRIMARY | film | ALL | NULL |
| 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id |
+----+--------------------+------------+--------+------------------------+
_8 dari tabel sambil menjalankan -- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];01 di atasnya secara bersamaan. Ini sebenarnya bukan batasan pengoptimal, tetapi mengetahui bagaimana MySQL mengeksekusi kueri dapat membantu Anda mengatasinya. Berikut adalah contoh kueri yang tidak diizinkan, meskipun itu adalah SQL standar. Kueri memperbarui setiap baris dengan jumlah baris serupa dalam tabel
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];_7
Untuk mengatasi batasan ini, Anda dapat menggunakan tabel turunan, karena MySQL mewujudkannya sebagai tabel sementara. Ini secara efektif mengeksekusi dua kueri. satu
mysql> EXPLAIN SELECT * FROM sakila.film ...;
+----+--------------------+------------+--------+------------------------+
| id | select_type | table | type | possible_keys |
+----+--------------------+------------+--------+------------------------+
| 1 | PRIMARY | film | ALL | NULL |
| 2 | DEPENDENT SUBQUERY | film_actor | eq_ref | PRIMARY,idx_fk_film_id |
+----+--------------------+------------+--------+------------------------+
_8 di dalam subkueri, dan satu multitabel -- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];01 dengan hasil gabungan dari tabel dan subkueri. Subkueri membuka dan menutup tabel sebelum
-- SELECT GROUP_CONCAT[film_id] FROM sakila.film_actor WHERE actor_id = 1; -- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN[1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980];01 luar membuka tabel, sehingga kueri sekarang akan berhasil