Pengoptimal MySQL mungkin tidak menggunakan indeks secara efektif saat memproses kueri kompleks yang mengandung ekspresi OR/IN, terutama dalam operasi JOIN multi-tabel. Hal ini dapat menyebabkan pemindaian tabel penuh dan menurunkan performa kueri. Fitur optimasi penulisan ulang kueri di PolarDB for MySQL menulis ulang ekspresi OR/IN yang memenuhi syarat menjadi struktur UNION ALL dan memilih jalur eksekusi optimal berdasarkan biaya. Dengan demikian, kueri dapat memanfaatkan indeks secara penuh dan meningkatkan performa eksekusi secara signifikan.
Cara Kerja
Di MySQL, pengoptimal memiliki kemampuan terbatas dalam menangani klausa OR. Ketika kondisi OR melibatkan beberapa tabel, pengoptimal sering kali hanya memperlakukannya sebagai kondisi filter setelah operasi join dan tidak dapat menggunakan indeks secara efektif untuk salah satu kondisi tersebut. Akibatnya, terjadi pemindaian tabel penuh yang menyebabkan penurunan tajam pada performa kueri.
Sebagai contoh, dalam kueri berikut, pengoptimal tidak dapat menggunakan indeks pada kolom t1.b atau t3.c1, sehingga hanya dapat melakukan pemindaian tabel penuh dan hash join—suatu pendekatan yang sangat tidak efisien.
-- Sebelum optimasi, rencana eksekusi adalah pemindaian tabel penuh, dan durasi eksekusi panjang.
EXPLAIN ANALYZE SELECT * FROM t1,t3 WHERE t3.c1 > 98 OR t1.b <= 0;
-> Filter: ((t3.c1 > 98) or (t1.b <= 0)) ... (actual time=115.259..5416.434 ...)
-> Inner hash join ...
-> Table scan on t3 ...
-> Hash
-> Table scan on t1 ...Secara logis, kueri OR ini setara dengan menggabungkan hasil dua kueri terpisah menggunakan UNION ALL. Jika ditulis ulang secara manual, kueri tersebut dapat memanfaatkan indeks masing-masing, sehingga performanya meningkat secara signifikan.
-- Ditulis ulang secara manual menjadi UNION ALL, rencana eksekusi dapat menggunakan indeks, dan durasi eksekusi jauh lebih singkat.
EXPLAIN ANALYZE
SELECT * FROM t1 ,t3 WHERE t1.b <= 0
UNION ALL
SELECT * FROM t1,t3 WHERE t3.c1 > 98 AND (t1.b > 0 OR (t1.b <= 0) IS NULL);
-> Append (actual time=58.272..302.546 ...)
...
-> Index range scan on t3 using idx_c1 ...Fitur PolarDB yang mengubah ekspresi OR/IN menjadi struktur UNION ALL mengotomatiskan proses optimasi manual ini. Selama fase pembuatan rencana, pengoptimal mengevaluasi potensi manfaat dari menulis ulang ekspresi OR menjadi struktur UNION ALL, membandingkan biayanya dengan rencana eksekusi asli, lalu memilih opsi dengan biaya lebih rendah untuk dieksekusi. Dengan demikian, kueri dipercepat tanpa memerlukan modifikasi SQL dari pengguna.
Penerapan
Seri produk: Cluster Edition, Standard Edition.
Versi mesin: MySQL 8.0.2, dan versi revisi harus 8.0.2.2.32 atau lebih baru.
Fitur ini sedang dalam rilis canary. Fitur ini diaktifkan secara default pada node read-only (RO) dan memerlukan pengaturan tambahan pada node read-write (RW). Untuk menggunakan fitur ini, Anda dapat membuat tiket.
Aktifkan dan Konfigurasikan Optimasi Penulisan Ulang Kueri
Anda dapat mengontrol perilaku fitur optimasi ini dengan mengatur parameter terkait.
Metode untuk mengubah parameter kluster PolarDB berbeda antara konsol dan sesi database, yaitu sebagai berikut:
Kompatibilitas: Untuk kompatibilitas dengan file konfigurasi MySQL, beberapa parameter kluster di Konsol PolarDB memiliki awalan loose_.
Prosedur: Temukan dan ubah parameter yang memiliki awalan
loose_.
Di sesi database (menggunakan command line atau client)
Prosedur: Saat menggunakan perintah
SETuntuk mengubah parameter dalam sesi database, hapus awalanloose_dan gunakan nama parameter aslinya.
Parameter | Tingkat | Deskripsi dan saran |
| Global/Sesi | Sakelar utama untuk fitur ini.
|
| Global/Sesi | Mengontrol ambang batas pemicu optimasi. Pengoptimal hanya mencoba penulisan ulang jika perkiraan biaya kueri asli—yang dapat Anda lihat menggunakan Rentang nilai: 0 hingga 18446744073709551615. Nilai default: 100000. Catatan Pertahankan nilai default. Jika Anda mengatur parameter ini ke |
Batasan
Fitur ini hanya dipicu jika semua kondisi berikut terpenuhi:
Batasan umum:
Jumlah parameter dalam klausa
ORatau IN-LIST tidak boleh melebihi 10.Blok kueri tidak boleh mengandung subkueri, klausa
GROUP BY, fungsi jendela, klausaDISTINCT, atau fungsi agregat.
Transformasi
UNION ALLumum (terutama untuk JOIN multi-tabel):Klausa
OR:Kondisi
ORharus melibatkan dua tabel atau lebih.Semua klausa
ORharus menggunakan polafield=constatau dapat menggunakan indeks secara efektif.Pola
field=const:fieldmerujuk pada kolom dalam tabel, danconstmerujuk pada nilai konstan.Penggunaan indeks efektif: Misalnya, dalam
t1.f1=t2.f2,f1adalah awalan indeks padat1danf2adalah awalan indeks padat2.
IN-LIST: Tidak perlu ditransformasi menjadiUNION ALLkarena metode aksesrangelebih optimal.
Transformasi
Top-K(terutama untuk kueriORDER BY...LIMITsatu tabel):Klausa
OR: KondisiORharus diterapkan pada kolom yang sama. Kolom ini dan kolomORDER BYharus merupakan awalan dari indeks yang sama. Misalnya, jika indeksnya adalah(c2, c3), maka kuerinya adalahWHERE c2=... OR c2=... ORDER BY c3.IN-LIST: Kolom pada ekspresi kiriIN-LISTdan kolomORDER BYharus merupakan awalan dari indeks yang sama.
Contoh: Verifikasi Efek Optimasi
Persiapan Data
-- Buat dan isi tabel t1
CREATE TABLE `t1` (
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
KEY `idx_a` (`a`)
) ENGINE=InnoDB;
-- Masukkan data
INSERT INTO `t1` VALUES (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(10,10);
-- Jalankan pernyataan ini berulang kali untuk menambah volume data
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
-- Buat dan isi tabel t3
CREATE TABLE `t3` (
`c1` int(11) NOT NULL,
`c2` int(11) DEFAULT NULL,
`c3` int(11) DEFAULT NULL,
`c4` int(11) DEFAULT NULL,
KEY `idx_c1`(`c1`),
KEY `idx_c2_c3` (`c2`,`c3`)
) ENGINE=InnoDB;
-- Masukkan data dalam jumlah besar
INSERT INTO `t3` VALUES (1,0,1,0),(2,0,2,0),(3,0,3,0),(4,0,4,0),(5,0,5,0),(6,0,6,0),(7,0,7,0),(8,0,8,0),(9,0,9,0),(10,0,10,0),(11,0,11,0),(12,0,12,0),(13,0,13,0),(14,0,14,0),(15,0,15,0),(16,0,16,0),(17,0,17,0),(18,0,18,0),(19,0,19,0),(20,0,20,0),(21,0,21,0),(22,0,22,0),(23,0,23,0),(24,0,24,0),(25,1,25,0),(26,1,26,0),(27,1,27,0),(28,1,28,0),(29,1,29,0),(30,1,30,0),(31,1,31,0),(32,1,32,0),(33,1,33,0),(34,1,34,0),(35,1,35,0),(36,1,36,0),(37,1,37,0),(38,1,38,0),(39,1,39,0),(40,1,40,0),(41,1,41,0),(42,1,42,0),(43,1,43,0),(44,1,44,0),(45,1,45,0),(46,1,46,0),(47,1,47,0),(48,1,48,0),(49,1,49,0),(50,1,50,1),(51,1,51,1),(52,1,52,1),(53,1,53,1),(54,1,54,1),(55,1,55,1),(56,1,56,1),(57,1,57,1),(58,1,58,1),(59,1,59,1),(60,1,60,1),(61,1,61,1),(62,1,62,1),(63,1,63,1),(64,1,64,1),(65,1,65,1),(66,1,66,1),(67,1,67,1),(68,1,68,1),(69,1,69,1),(70,1,70,1),(71,1,71,1),(72,1,72,1),(73,1,73,1),(74,1,74,1),(75,2,75,1),(76,2,76,1),(77,2,77,1),(78,2,78,1),(79,2,79,1),(80,2,80,1),(81,2,81,1),(82,2,82,1),(83,2,83,1),(84,2,84,1),(85,2,85,1),(86,2,86,1),(87,2,87,1),(88,2,88,1),(89,2,89,1),(90,2,90,1),(91,2,91,1),(92,2,92,1),(93,2,93,1),(94,2,94,1),(95,2,95,1),(96,2,96,1),(97,2,97,1),(98,2,98,1),(99,2,99,1),(100,2,100,1);
-- Jalankan pernyataan ini berulang kali untuk menambah volume data
INSERT INTO t3 SELECT * FROM t3;
INSERT INTO t3 SELECT * FROM t3;
INSERT INTO t3 SELECT * FROM t3;
INSERT INTO t3 SELECT * FROM t3;
INSERT INTO t3 SELECT * FROM t3;
INSERT INTO t3 SELECT * FROM t3;
-- Analisis tabel
ANALYZE TABLE t1, t3;Skenario 1: Optimalkan Kueri JOIN Multi-Tabel
Skenario ini menunjukkan bagaimana pengoptimal menggunakan penulisan ulang untuk memanfaatkan indeks ketika kondisi OR mencakup dua tabel.
Nonaktifkan fitur optimasi dan amati rencana eksekusi asli.
-- Nonaktifkan fitur optimasi SET polar_optimizer_switch='or_expansion=off'; -- Analisis pernyataan DESC SELECT * FROM t1,t3 WHERE t3.c1 >98 OR t1.a<5;Analisis hasil: Rencana eksekusi menunjukkan
Hash Joindan pemindaian tabel penuh padat1sertat3. Pengoptimal gagal menggunakan indeks padat1.adant3.c1.+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+ | 1 | SIMPLE | t1 | NULL | ALL | idx_a | NULL | NULL | NULL | 1280 | 100.00 | NULL | | 1 | SIMPLE | t3 | NULL | ALL | idx_c1 | NULL | NULL | NULL | 6591 | 100.00 | Using where; Using join buffer (hash join) | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+Aktifkan fitur optimasi dan lihat rencana eksekusi yang ditulis ulang.
-- Aktifkan fitur optimasi SET polar_optimizer_switch='or_expansion=on'; -- Turunkan ambang batas untuk memicu optimasi SET cbqt_cost_threshold=1; -- Analisis pernyataan DESC SELECT * FROM t1,t3 WHERE t3.c1 >98 OR t1.a<5;Analisis hasil: Rencana eksekusi disesuaikan untuk menggunakan
UNION ALL, sehingga kueri dapat memanfaatkan indeks padat1.adant3.c1, mencapai efek yang sama seperti penulisan ulang manual keUNION ALL.+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------------------------+ | 1 | PRIMARY | t1 | NULL | range | idx_a | idx_a | 5 | NULL | 256 | 100.00 | Using index condition; Using MRR | | 1 | PRIMARY | t3 | NULL | ALL | NULL | NULL | NULL | NULL | 6400 | 100.00 | Using join buffer (hash join) | | 2 | UNION | t3 | NULL | range | idx_c1 | idx_c1 | 4 | NULL | 128 | 100.00 | Using index condition; Using MRR | | 2 | UNION | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 640 | 66.67 | Using where; Using join buffer (hash join) | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------------------------+
Skenario 2: Optimalkan Kueri Top-K (Klausa OR)
Skenario ini menunjukkan bagaimana pengoptimal menulis ulang kondisi OR menjadi UNION ALL dan mendorong klausa LIMIT ke bawah untuk kueri ORDER BY ... LIMIT satu tabel, sehingga menghindari pengurutan skala besar.
Nonaktifkan fitur optimasi dan amati rencana eksekusi asli.
-- Nonaktifkan fitur optimasi SET polar_optimizer_switch='or_expansion=off'; -- Analisis pernyataan DESC ANALYZE SELECT c2 FROM t3 WHERE (c2 = 2 OR c2= 0 ) ORDER BY t3.c3 DESC LIMIT 5;Analisis hasil: Rencana eksekusi menggunakan
Index range scanuntuk mengambil semua baris yang memenuhi kondisic2=2atauc2=0, lalu melakukan operasiSort. Durasi eksekusi sekitar 200 milidetik.+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | EXPLAIN | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | -> Limit: 5 row(s) (actual time=193.389..193.393 rows=5 loops=1) -> Sort: t3.c3 DESC, limit input to 5 row(s) per chunk (cost=641.82 rows=3200) (actual time=193.386..193.388 rows=5 loops=1) -> Index range scan on t3 using idx_c2_c3, with index condition: ((t3.c2 = 2) or (t3.c2 = 0)) (actual time=0.348..187.455 rows=3200 loops=1) | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.20 sec)Aktifkan fitur optimasi dan lihat rencana eksekusi yang ditulis ulang.
-- Nonaktifkan fitur optimasi SET polar_optimizer_switch='or_expansion=on'; -- Turunkan ambang batas untuk memicu optimasi SET cbqt_cost_threshold=1; -- Pernyataan analitik DESC ANALYZE SELECT c2 FROM t3 WHERE (c2 = 2 OR c2= 0 ) ORDER BY t3.c3 DESC LIMIT 5;Analisis hasil: Rencana eksekusi berubah untuk menggunakan
UNION ALL. Pengoptimal melakukanIndex lookupdan menerapkanLIMIT 5pada setiap cabang (c2=2danc2=0), lalu menggabungkan dua set hasil terurut berisi 5 baris tersebut, sehingga menghilangkan kebutuhan pengurutan global. Durasi eksekusi turun menjadi sekitar 1 milidetik.+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | EXPLAIN | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | -> Limit: 5 row(s) (actual time=1.249..1.254 rows=5 loops=1) -> Sort: derived_1_2.Name_exp_1 DESC, limit input to 5 row(s) per chunk (actual time=0.104..0.106 rows=5 loops=1) -> Table scan on derived_1_2 (actual time=0.006..0.013 rows=10 loops=1) -> Union materialize (actual time=1.246..1.249 rows=5 loops=1) -> Limit: 5 row(s) (actual time=0.336..0.571 rows=5 loops=1) -> Index lookup on t3 using idx_c2_c3 (c2=2; iterate backwards) (cost=0.00 rows=5) (actual time=0.333..0.566 rows=5 loops=1) -> Limit: 5 row(s) (actual time=0.215..0.431 rows=5 loops=1) -> Index lookup on t3 using idx_c2_c3 (c2=0; iterate backwards) (cost=0.00 rows=5) (actual time=0.214..0.427 rows=5 loops=1) | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.01 sec)
Skenario 3: Optimalkan Kueri Top-K (IN-LIST)
IN-LIST secara logis setara dengan klausa OR, sehingga juga mendukung optimasi Top-K.
Nonaktifkan fitur optimasi dan amati rencana eksekusi asli.
-- Nonaktifkan fitur optimasi SET polar_optimizer_switch='or_expansion=off'; -- Analisis pernyataan DESC ANALYZE SELECT c2 FROM t3 WHERE c2 IN (2, 0) ORDER BY t3.c3 DESC LIMIT 5;Analisis hasil: Rencana eksekusi menggunakan
Index range scanuntuk mengambil semua baris yang memenuhi kondisit3.c2 in (2,0), lalu melakukan operasiSort. Durasi eksekusi sekitar 200 milidetik.+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | EXPLAIN | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | -> Limit: 5 row(s) (actual time=197.497..197.501 rows=5 loops=1) -> Sort: t3.c3 DESC, limit input to 5 row(s) per chunk (cost=641.82 rows=3200) (actual time=197.494..197.496 rows=5 loops=1) -> Index range scan on t3 using idx_c2_c3, with index condition: (t3.c2 in (2,0)) (actual time=0.319..191.560 rows=3200 loops=1) | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.20 sec)Aktifkan fitur optimasi dan lihat rencana eksekusi yang ditulis ulang.
-- Nonaktifkan fitur optimasi SET polar_optimizer_switch='or_expansion=on'; -- Turunkan ambang batas untuk memicu optimasi SET cbqt_cost_threshold=1; -- Pernyataan analitik DESC ANALYZE SELECT c2 FROM t3 WHERE c2 IN (2, 0) ORDER BY t3.c3 DESC LIMIT 5;Analisis hasil: Rencana eksekusi berubah untuk menggunakan
UNION ALL. Pengoptimal melakukanIndex lookupdan menerapkanLIMIT 5pada setiap cabang (c2=2danc2=0), lalu menggabungkan dua set hasil terurut berisi 5 baris tersebut, sehingga menghilangkan kebutuhan pengurutan global.+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | EXPLAIN | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | -> Limit: 5 row(s) (actual time=1.256..1.260 rows=5 loops=1) -> Sort: derived_1_2.Name_exp_1 DESC, limit input to 5 row(s) per chunk (actual time=0.090..0.093 rows=5 loops=1) -> Table scan on derived_1_2 (actual time=0.005..0.012 rows=10 loops=1) -> Union materialize (actual time=1.252..1.255 rows=5 loops=1) -> Limit: 5 row(s) (actual time=0.259..0.545 rows=5 loops=1) -> Index lookup on t3 using idx_c2_c3 (c2=2; iterate backwards) (cost=0.00 rows=5) (actual time=0.256..0.540 rows=5 loops=1) -> Limit: 5 row(s) (actual time=0.237..0.455 rows=5 loops=1) -> Index lookup on t3 using idx_c2_c3 (c2=0; iterate backwards) (cost=0.00 rows=5) (actual time=0.236..0.451 rows=5 loops=1) +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
Gunakan HINT untuk Intervensi Manual
Dalam skenario tertentu, Anda dapat menggunakan HINT untuk mengontrol apakah optimasi ini diaktifkan untuk satu kueri tertentu.
NO_OR_EXPAND(@QB_NAME): Memaksa menonaktifkan optimasi ekspansiORuntuk blok kueri yang ditentukan.DESC SELECT /*+NO_OR_EXPAND(@subq1) */ * FROM t1 WHERE EXISTS (SELECT /*+ QB_NAME(subq1) */ 1 FROM t3 WHERE (t1.a = 1 OR t1.b = 2) AND t3.c1 < 5 AND t1.b = t3.c1); +----+-------------+-------+------------+------+---------------+--------+---------+------------+------+----------+-----------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+--------+---------+------------+------+----------+-----------------------------+ | 1 | SIMPLE | t1 | NULL | ALL | idx_a | NULL | NULL | NULL | 640 | 19.00 | Using where | | 1 | SIMPLE | t3 | NULL | ref | idx_c1 | idx_c1 | 4 | test2.t1.b | 64 | 100.00 | Using index; FirstMatch(t1) | +----+-------------+-------+------------+------+---------------+--------+---------+------------+------+----------+-----------------------------+Jika klausa
WHEREmengandung beberapa ekspresiOR, Anda dapat menggunakanOR_EXPAND(@QB_NAME idx)untuk memaksa ekspresi tertentu ditransformasi menjadi strukturUNION ALL. Parameteridxmenentukan posisi ekspresi dalam klausaWHERE, dengan indeks dimulai dari 0. Dalam contoh ini, ekspresi(t3.c2 = 1 OR t1.b = 2)diekspansi menjadi strukturUNION ALL.DESC format=tree SELECT /*+OR_EXPAND(@subq1 3) */ * FROM t1 WHERE EXISTS (SELECT /*+ QB_NAME(subq1) */ 1 FROM t3 WHERE (t3.c2 = 999 OR t1.b = 999) AND t3.c1 < 5 AND t1.b = t3.c1 AND (t3.c2= 1 OR t1.b = 2)); +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | EXPLAIN | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | -> Filter: exists(select #2) (cost=64.75 rows=640) -> Table scan on t1 (cost=64.75 rows=640) -> Select #2 (subquery in condition; dependent) -> Limit: 1 row(s) -> Append -> Stream results -> Filter: (t3.c2 = 1) (cost=17.45 rows=32) -> Index lookup on t3 using idx_c1 (c1=t1.b), with index condition: ((t1.b = 999) and (t3.c1 < 5)) (cost=17.45 rows=64) -> Stream results -> Filter: (t3.c1 = 2) (cost=0.51 rows=0) -> Index lookup on t3 using idx_c2_c3 (c2=999), with index condition: ((t1.b = 2) and lnnvl((t3.c2 = 1))) (cost=0.51 rows=1)OR_EXPAND(@QB_NAME): Memaksa mengaktifkan optimasi ekspansiORuntuk blok kueri yang ditentukan (qb_name).DESC SELECT /*+OR_EXPAND(@subq1) */ * FROM t1 WHERE EXISTS (SELECT /*+ QB_NAME(subq1) */ 1 FROM t3 WHERE (t3.c1 = 1 OR t1.b = 2) AND t3.c1 < 5 AND t1.b = t3.c1); +----+--------------------+-------+------------+------+---------------+--------+---------+-------+------+----------+------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------------+-------+------------+------+---------------+--------+---------+-------+------+----------+------------------------------------+ | 1 | PRIMARY | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 640 | 100.00 | Using where | | 2 | DEPENDENT SUBQUERY | t3 | NULL | ref | idx_c1 | idx_c1 | 4 | const | 64 | 100.00 | Using where; Using index | | 3 | DEPENDENT UNION | t3 | NULL | ref | idx_c1 | idx_c1 | 4 | const | 64 | 100.00 | Using index condition; Using index | +----+--------------------+-------+------------+------+---------------+--------+---------+-------+------+----------+------------------------------------+