全部产品
Search
文档中心

PolarDB:Mengubah Ekspresi OR/IN Menjadi Struktur UNION ALL

更新时间:Dec 04, 2025

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.

Catatan

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:

  • Di Konsol PolarDB

    • 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 SET untuk mengubah parameter dalam sesi database, hapus awalan loose_ dan gunakan nama parameter aslinya.

Parameter

Tingkat

Deskripsi dan saran

loose_polar_optimizer_switch

Global/Sesi

Sakelar utama untuk fitur ini.

  • or_expansion=on (default): Mengaktifkan fitur.

  • or_expansion=off: Menonaktifkan fitur.

loose_cbqt_cost_threshold

Global/Sesi

Mengontrol ambang batas pemicu optimasi. Pengoptimal hanya mencoba penulisan ulang jika perkiraan biaya kueri asli—yang dapat Anda lihat menggunakan EXPLAIN—melebihi nilai ini.

Rentang nilai: 0 hingga 18446744073709551615.

Nilai default: 100000.

Catatan

Pertahankan nilai default. Jika Anda mengatur parameter ini ke 0, pengoptimal akan mencoba menulis ulang semua kueri yang memenuhi syarat. Hal ini dapat meningkatkan waktu optimasi untuk kueri sederhana dan memengaruhi performa bisnis.

Batasan

Fitur ini hanya dipicu jika semua kondisi berikut terpenuhi:

  • Batasan umum:

    • Jumlah parameter dalam klausa OR atau IN-LIST tidak boleh melebihi 10.

    • Blok kueri tidak boleh mengandung subkueri, klausa GROUP BY, fungsi jendela, klausa DISTINCT, atau fungsi agregat.

  • Transformasi UNION ALL umum (terutama untuk JOIN multi-tabel):

    • Klausa OR:

      • Kondisi OR harus melibatkan dua tabel atau lebih.

      • Semua klausa OR harus menggunakan pola field=const atau dapat menggunakan indeks secara efektif.

        • Pola field=const: field merujuk pada kolom dalam tabel, dan const merujuk pada nilai konstan.

        • Penggunaan indeks efektif: Misalnya, dalam t1.f1=t2.f2, f1 adalah awalan indeks pada t1 dan f2 adalah awalan indeks pada t2.

    • IN-LIST: Tidak perlu ditransformasi menjadi UNION ALL karena metode akses range lebih optimal.

  • Transformasi Top-K (terutama untuk kueri ORDER BY...LIMIT satu tabel):

    • Klausa OR: Kondisi OR harus diterapkan pada kolom yang sama. Kolom ini dan kolom ORDER BY harus merupakan awalan dari indeks yang sama. Misalnya, jika indeksnya adalah (c2, c3), maka kuerinya adalah WHERE c2=... OR c2=... ORDER BY c3.

    • IN-LIST: Kolom pada ekspresi kiri IN-LIST dan kolom ORDER BY harus 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.

  1. 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 Join dan pemindaian tabel penuh pada t1 serta t3. Pengoptimal gagal menggunakan indeks pada t1.a dan 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 | 1280 |   100.00 | NULL                                       |
    |  1 | SIMPLE      | t3    | NULL       | ALL  | idx_c1        | NULL | NULL    | NULL | 6591 |   100.00 | Using where; Using join buffer (hash join) |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+
  2. 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 pada t1.a dan t3.c1, mencapai efek yang sama seperti penulisan ulang manual ke UNION 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.

  1. 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 scan untuk mengambil semua baris yang memenuhi kondisi c2=2 atau c2=0, lalu melakukan operasi Sort. 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)
  2. 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 melakukan Index lookup dan menerapkan LIMIT 5 pada setiap cabang (c2=2 dan c2=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.

  1. 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 scan untuk mengambil semua baris yang memenuhi kondisi t3.c2 in (2,0), lalu melakukan operasi Sort. 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)
  2. 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 melakukan Index lookup dan menerapkan LIMIT 5 pada setiap cabang (c2=2 dan c2=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 ekspansi OR untuk 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 WHERE mengandung beberapa ekspresi OR, Anda dapat menggunakan OR_EXPAND(@QB_NAME idx) untuk memaksa ekspresi tertentu ditransformasi menjadi struktur UNION ALL. Parameter idx menentukan posisi ekspresi dalam klausa WHERE, dengan indeks dimulai dari 0. Dalam contoh ini, ekspresi (t3.c2 = 1 OR t1.b = 2) diekspansi menjadi struktur UNION 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 ekspansi OR untuk 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 |
    +----+--------------------+-------+------------+------+---------------+--------+---------+-------+------+----------+------------------------------------+