すべてのプロダクト
Search
ドキュメントセンター

PolarDB:OR/IN 式の UNION ALL への変換

最終更新日:Dec 04, 2025

MySQL オプティマイザーは、OR/IN 式を含む複雑なクエリ、特に複数テーブルの JOIN 操作を処理する際に、インデックスを効果的に使用できない場合があります。これにより、全表スキャンが実行され、クエリパフォーマンスが低下する可能性があります。PolarDB for MySQL のクエリ書き換え最適化機能は、対象の OR/IN 式を UNION ALL 構造に書き換え、コストに基づいて最適な実行パスを選択します。これにより、クエリでインデックスを最大限に活用でき、実行パフォーマンスが大幅に向上します。

仕組み

MySQL では、オプティマイザーは OR 句を処理する際に機能が制限されます。OR 条件に複数のテーブルが含まれる場合、オプティマイザーは多くの場合、それを JOIN 操作後のフィルター条件としてのみ扱います。単一の条件に対してインデックスを効果的に使用することはできません。これにより、全表スキャンが発生し、クエリパフォーマンスが大幅に低下する可能性があります。

たとえば、次のクエリでは、オプティマイザーは t1.b または t3.c1 カラムのインデックスを使用できないため、非常に非効率な全表スキャンとハッシュ結合しか実行できません。

-- Before optimization, the execution plan is a full table scan, and the execution duration is long.
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 ...

論理的に、この OR クエリは、UNION ALL を使用して 2 つの個別のクエリの結果をマージすることと同等です。クエリを手動で再書き込みすると、それぞれのインデックスが使用可能になり、パフォーマンスが大幅に向上します。

-- Manually rewritten to UNION ALL, the execution plan can use indexes, and the execution duration is significantly shorter.
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 ...

OR/IN 式を UNION ALL 構造に変換する PolarDB の機能は、この手動の最適化プロセスを自動化します。実行計画の生成フェーズにおいて、オプティマイザーは OR 式を UNION ALL 構造に書き換えることによるメリットを評価します。元の実行計画とコストを比較し、より低コストのオプションを実行用に選択します。これにより、SQL を変更することなくクエリが高速化されます。

適用範囲

  • 製品シリーズ:Cluster EditionStandard Edition

  • エンジンバージョン:MySQL 8.0.2、リビジョンバージョンは 8.0.2.2.32 以降である必要があります。

説明

この機能はカナリアリリース中です。読み取り専用 (RO) ノードではデフォルトで有効になっていますが、読み書き (RW) ノードでは追加の設定が必要です。この機能を使用するには、チケットを送信してください。

クエリ再書き込み最適化の有効化と設定

関連するパラメーターを設定することで、この最適化機能の動作を制御できます。

PolarDB クラスターのパラメーターを変更する方法は、コンソールとデータベースセッションで異なります。違いは次のとおりです。

  • PolarDB コンソールの場合

    • 互換性:MySQL 設定ファイルとの互換性のため、PolarDB コンソールの一部のクラスターパラメーターには loose_ プレフィックスが付いています。

    • 手順loose_ プレフィックスが付いているパラメーターを検索して変更します。

  • データベースセッション (コマンドラインまたはクライアントを使用) の場合

    • 手順: データベースセッションで SET コマンドを使用してパラメーターを変更する場合、loose_ プレフィックスを取り除き、元のパラメーター名を使用します。

パラメーター

レベル

説明と提案

loose_polar_optimizer_switch

グローバル/セッション

この機能のメインスイッチです。

  • or_expansion=on (デフォルト):機能を有効にします。

  • or_expansion=off:機能を無効にします。

loose_cbqt_cost_threshold

グローバル/セッション

最適化トリガーのしきい値を制御します。オプティマイザーは、EXPLAIN を使用して表示できる元のクエリの推定コストがこの値を超えた場合にのみ、再書き込みを試みます。

値の範囲:0 から 18446744073709551615。

デフォルト値:100000。

説明

デフォルト値のままにしてください。このパラメーターを 0 に設定すると、オプティマイザーはすべての書き換え対象のクエリの再書き込みを試みます。これにより、シンプルなクエリの最適化時間が増加し、ビジネスパフォーマンスに影響を与える可能性があります。

制限事項

この機能は、以下のすべての条件が満たされた場合にのみトリガーされます。

  • 一般的な制限:

    • OR 句または IN-LIST のパラメーターの数は 10 を超えることはできません。

    • クエリブロックには、サブクエリ、GROUP BY 句、ウィンドウ関数、DISTINCT 句、または集計関数を含めることはできません。

  • 一般的な UNION ALL 変換 (主に複数テーブルの JOIN 用):

    • OR 句:

      • OR 条件は、2 つ以上のテーブルに関連している必要があります。

      • すべての OR 句は、field=const パターンを使用するか、インデックスを効果的に使用できる必要があります。

        • field=const パターン: field はテーブルの列を指し、const は定数値を指します。

        • インデックスの効果的な使用: たとえば、 t1.f1=t2.f2 では、 f1t1 のインデックスのプレフィックスであり、 f2t2 のインデックスのプレフィックスです。

    • IN-LIST: range アクセス方式の方が最適であるため、UNION ALL に変換する必要はありません。

  • Top-K 変換 (主に単一テーブルの ORDER BY...LIMIT クエリ):

    • OR 句: OR 条件は同じカラムに適用する必要があります。このカラムと ORDER BY カラムは、同じインデックスのプレフィックスである必要があります。たとえば、インデックスが (c2, c3) の場合、クエリは WHERE c2=... OR c2=... ORDER BY c3 となります。

    • IN-LISTIN-LIST の左側の式の列と ORDER BY 列は、同じインデックスのプレフィックスである必要があります。

例:最適化効果の検証

データ準備

-- Create and populate table t1
CREATE TABLE `t1` (
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  KEY `idx_a` (`a`)
) ENGINE=InnoDB;
-- Insert 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);
-- Run this statement repeatedly to increase the data volume
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;


-- Create and populate table 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;
-- Insert a large amount of data
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);
-- Run this statement repeatedly to increase the data volume
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;

-- Analyze the tables
ANALYZE TABLE t1, t3;

シナリオ 1:複数テーブルの JOIN クエリの最適化

このシナリオは、OR 条件が 2 つのテーブルにまたがる場合に、オプティマイザーが書き換えによってインデックスを活用する方法を示しています。

  1. 最適化機能を無効にし、元の実行計画を確認します。

    -- Disable the optimization feature
    SET polar_optimizer_switch='or_expansion=off';
    -- Analyze the statement
    DESC SELECT * FROM t1,t3 WHERE t3.c1 >98 OR t1.a<5;

    結果分析: 実行計画には、Hash Join と、t1 および t3 での全表スキャンが示されています。オプティマイザーは、t1.at3.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. 最適化機能を有効にし、書き換えられた実行計画を表示します。

    -- Enable the optimization feature
    SET polar_optimizer_switch='or_expansion=on';
    -- Lower the threshold to trigger the optimization
    SET cbqt_cost_threshold=1;
    -- Analyze the statement
    DESC SELECT * FROM t1,t3 WHERE t3.c1 >98 OR t1.a<5;

    結果分析:実行計画が UNION ALL を使用するように調整されたことで、クエリは t1.at3.c1 のインデックスを使用できるようになり、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) |
    +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------------------------+

シナリオ 2:Top-K クエリの最適化 (OR 句)

このシナリオでは、オプティマイザーが単一テーブルの ORDER BY ... LIMIT クエリに対して OR 条件を UNION ALL に再書き込みし、LIMIT 句をプッシュダウンする方法を示します。このプロセスにより、大規模なソートが回避されます。

  1. 最適化機能を無効にし、元の実行計画を確認します。

    -- Disable the optimization feature
    SET polar_optimizer_switch='or_expansion=off';
    -- Analyze the statement
    DESC ANALYZE SELECT c2 FROM t3 WHERE (c2 = 2 OR c2= 0 ) ORDER BY t3.c3 DESC LIMIT 5;

    結果分析: 実行計画は、Index range scan を使用して c2=2 または c2=0 の条件を満たすすべての行を取得し、次に Sort 操作を実行します。実行時間は約 200 ミリ秒です。

    +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | 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. 最適化機能を有効にし、書き換えられた実行計画を表示します。

    -- Disable the optimization feature
    SET polar_optimizer_switch='or_expansion=on';
    -- Lower the threshold to trigger the optimization
    SET cbqt_cost_threshold=1;
    -- Analytic statement
    DESC ANALYZE SELECT c2 FROM t3 WHERE (c2 = 2 OR c2= 0 ) ORDER BY t3.c3 DESC LIMIT 5;

    結果分析: 実行計画は UNION ALL を使用するように変更されます。各ブランチ (c2=2 および c2=0) に対して Index lookup を実行し、LIMIT 5 を適用します。次に、オプティマイザーが 2 つのソートされた 5 行の結果セットをマージするため、グローバルソートは不要になります。実行時間は約 1 ミリ秒に短縮されます。

    +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | 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)

シナリオ 3:Top-K クエリの最適化 (IN-LIST)

IN-LISTOR 句と論理的に等価であるため、Top-K 最適化もサポートします。

  1. 最適化機能を無効にし、元の実行計画を確認します。

    -- Disable the optimization feature
    SET polar_optimizer_switch='or_expansion=off';
    -- Analyze the statement
    DESC ANALYZE SELECT c2 FROM t3 WHERE c2 IN (2, 0) ORDER BY t3.c3 DESC LIMIT 5;

    結果分析: 実行計画は、Index range scan を使用して t3.c2 in (2,0) 条件を満たすすべての行を取得し、次に Sort 操作を実行します。実行時間は約 200 ミリ秒です。

    +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | 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. 最適化機能を有効にし、書き換えられた実行計画を表示します。

    -- Disable the optimization feature
    SET polar_optimizer_switch='or_expansion=on';
    -- Lower the threshold to trigger the optimization
    SET cbqt_cost_threshold=1;
    -- Analytic statement
    DESC ANALYZE SELECT c2 FROM t3 WHERE c2 IN (2, 0) ORDER BY t3.c3 DESC LIMIT 5;

    結果分析: 実行計画は UNION ALL を使用するように変更されます。各ブランチ (c2=2 および c2=0) に対して Index lookup を実行し、LIMIT 5 を適用します。その後、オプティマイザーは 2 つのソート済みの 5 行の結果セットをマージするため、グローバルソートが不要になります。

    +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | 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)

手動介入のためのヒントの使用

特定のシナリオでは、HINT を使用して、単一の クエリに対してこの最適化を有効にするかどうかを制御できます。

  • NO_OR_EXPAND(@QB_NAME): 指定されたクエリブロックの OR 展開最適化を強制的に無効にします。

     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) |
    +----+-------------+-------+------------+------+---------------+--------+---------+------------+------+----------+-----------------------------+

    WHERE 句に複数の OR 式が含まれている場合、OR_EXPAND(@QB_NAME idx) を使用して、特定の式を強制的に UNION ALL 構造に変換できます。 idx パラメーターは、WHERE 句における式の位置を指定します。インデックスは 0 から始まります。 この例では、式 (t3.c2 = 1 OR t1.b = 2)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): OR 展開の最適化を、指定されたクエリブロック (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 |
    +----+--------------------+-------+------------+------+---------------+--------+---------+-------+------+----------+------------------------------------+