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

PolarDB:サブクエリフォールディングの使い方

最終更新日:Mar 29, 2026

サブクエリフォールディングは、重複するサブクエリをマージまたは削除することで SQL ステートメント内のサブクエリ数を削減し、クエリ結果を変更せずに実行時のオーバーヘッドを低減します。

仕組み

2 つのサブクエリが重複するデータセットを対象としている場合、オプティマイザーはそれらを削除または結合できます。

  • 削除 — 一方のサブクエリの結果が他方によって保証されるため、完全に削除されます。

  • マージ — 2 つのサブクエリの条件を 1 つのサブクエリに統合します。

オプティマイザーは、サブクエリペアの以下の 2 つの特性に基づいてフォールディングルールを適用します:その タイプ関係(同一タイプまたは相互排他)およびその 包含関係(結果セット間の関係)です。

基本概念

サポートされるサブクエリのタイプ

タイプ演算子
EXISTSEXISTSNOT EXISTSWHERE EXISTS (SELECT 1 FROM t2)
ININNOT INWHERE a IN (SELECT a FROM t2)
ANY= ANY!= ANY< ANY<= ANY> ANY>= ANYWHERE t.a > ANY (SELECT t2.a FROM t2)
ALL= ALL!= ALL< ALL<= ALL> ALL>= ALLWHERE t.a > ANY (SELECT t2.a FROM t2)
単一行スカラーサブクエリ(例:WHERE t.a < (SELECT MIN(t2.a) ...))はサポートされていません。

同一タイプおよび相互に排他的なサブクエリ

  • 同一タイプのサブクエリは、同じ演算子を使用します。EXISTS サブクエリ同士、または > ANY サブクエリ同士は、同一タイプのサブクエリです。

  • 相互に排他的なサブクエリは、相反する演算子を使用します。以下の表に、すべての相互に排他的なペアを示します。

サブクエリ相互に排他的なサブクエリ
EXISTSNOT EXISTS
INNOT IN
= ANY!= ALL
!= ANY= ALL
< ANY>= ALL または > ALL
<= ANY> ALL
> ANY<= ALL または < ALL
>= ANY< ALL

これらの等価性は、基礎となる論理的恒等性を反映しています:IN= ANY と等価であり、NOT IN!= ALL と等価です。これらを理解することで、相互に排他的なペアを直感的に把握しやすくなります。

包含関係

サブクエリの右側はセットです。2 つのセットには、以下の 4 種類の関係があります。

関係意味
左部分集合左のセットが右のセットの真部分集合である
右部分集合右のセットが左のセットの真部分集合である
等しい2 つのセットが同一の要素を含む
比較不能どちらのセットも他方を含まない

左部分集合の例: 次のクエリでは、subq1 は追加条件 t2.a > 10 を適用しているため、その結果は常に subq2 の結果の部分集合となります。

SELECT a FROM t
WHERE EXISTS (SELECT /*+ subq1 */ t2.a FROM t2 WHERE t2.a > 10)   -- subq1
  AND EXISTS (SELECT /*+ subq2 */ t2.a FROM t2);                   -- subq2

前提条件

開始する前に、以下の条件を満たしていることを確認してください。

  • PolarDB for MySQL 8.0 クラスターのリビジョンバージョンが 8.0.2.2.23 以降であること

クラスターのバージョンを確認するには、「エンジンバージョン(5.6、5.7、8.0)」をご参照ください。

サブクエリフォールディングの有効化

フォールディング動作を制御するパラメーターは 2 つあります。

パラメータースコープデフォルト値説明
loose_polar_optimizer_switchグローバルcoalesce_subquery=offサブクエリフォールディングを有効または無効にします。coalesce_subquery=on に設定すると有効になります。
force_coalesce_subqueryグローバル/セッションOFF「常に最適とは限らない」とマークされたマージ操作を強制的に実行します。通常、オプティマイザーのコストベースクエリ変換(CBQT)コンポーネントがマージによるパフォーマンス向上を判断しますが、このパラメーターを ON に設定すると、そのチェックをバイパスします。

サブクエリフォールディングを有効にするには:

SET loose_polar_optimizer_switch = 'coalesce_subquery=on';

現在のセッションでサブクエリのマージを強制するには(マージが有益であることを事前に確認した場合のみ使用):

SET force_coalesce_subquery = ON;

HINT 構文を用いて特定のサブクエリを対象とするにはQB_NAME でクエリブロックに名前を付け、SUBQUERY_COALESCE でマージ対象のペアを指定します。

DESC SELECT /*+ SUBQUERY_COALESCE(qb1, qb2) SUBQUERY_COALESCE(qb3, qb4) */
  *
FROM t1
LEFT JOIN t2 ON t1.a = ANY  (SELECT /*+ QB_NAME(qb1) */ a FROM t2)
           AND t1.a != ALL  (SELECT /*+ QB_NAME(qb2) */ a FROM t2 WHERE a < 100)
HAVING     t1.b  = ANY      (SELECT /*+ QB_NAME(qb3) */ b FROM t2)
       AND t1.b != ALL      (SELECT /*+ QB_NAME(qb4) */ b FROM t2 WHERE b < 1);
フォールドされたオブジェクトは、WHEREHAVING、または JOIN ON 条件の任意の位置に出現可能です。これには、AND および OR 演算子の下も含まれます。

フォールディングルール

同一タイプのサブクエリ

AND 演算子

サブクエリのタイプ包含関係結果
両方:EXISTS、IN、ANY、または ALL左部分集合または等しい削除 — 右側のサブクエリを削除し、左側を保持
両方:EXISTS、IN、ANY、または ALL右部分集合削除 — 左側のサブクエリを削除し、右側を保持
両方:NOT EXISTS、NOT IN、または != ALL比較不能マージ(常に最適とは限らない)— WHERE または HAVING 条件を 1 つのサブクエリに統合します。SPJ サブクエリ、または SPJ および HAVING 条件のみを含むサブクエリが必要です。WHERE 条件のみを含むサブクエリや、一貫性のない HAVING 条件を含むサブクエリもサポートされます。

OR 演算子

サブクエリのタイプ包含関係結果
両方:EXISTS、IN、ANY、または ALL左部分集合または等しい削除 — 左側のサブクエリを削除し、右側を保持
両方:EXISTS、IN、ANY、または ALL右部分集合削除 — 右側のサブクエリを削除し、左側を保持
両方:EXISTS、IN、または ANY比較不能マージ(常に最適とは限らない)— WHERE または HAVING 条件を 1 つのサブクエリに統合します。SPJ サブクエリ、または SPJ および HAVING 条件のみを含むサブクエリが必要です。WHERE 条件のみを含むサブクエリや、一貫性のない HAVING 条件を含むサブクエリもサポートされます。

相互に排他的なサブクエリ

AND 演算子

サブクエリのタイプ包含関係マージ条件結果
EXISTS + NOT EXISTS;IN + NOT IN左部分集合または等しい[削除] — AND 条件は FALSE
EXISTS + NOT EXISTS右部分集合クエリブロックは UNION できない;WHERE 条件のみが異なる;ネストされたサブクエリをサポートマージ(常に最適とは限らない)— セットを統合し、HAVING SUM(CASE WHEN extra_cond THEN 1 ELSE 0 END) ==0 を追加
!= ANY= ALL< ANY>= ALL または > ALL<= ANY> ALL> ANY<= ALL または < ALL>= ANY< ALL左部分集合または等しい削除 — AND 条件が FALSE
IN + NOT IN;= ANY!= ALL右部分集合クエリブロックは UNION できない;WHERE または HAVING 条件のみが異なる;ネストされたサブクエリをサポートマージ(常に最適)— セットを統合し、LNNVL 演算子を追加。デフォルトで適用され、force_coalesce_subquery の設定は不要です。

OR 演算子

サブクエリのタイプ包含関係結果
EXISTS + NOT EXISTS右部分集合削除 — OR 条件を TRUE

同一タイプのサブクエリの削除

AND 条件

-- 前
SELECT * FROM t1
WHERE EXISTS (SELECT 1 FROM t2 WHERE c2 = 0)   -- サブクエリ 1
  AND EXISTS (SELECT 1 FROM t2);               -- サブクエリ 2

サブクエリ 1 はサブクエリ 2 の部分集合であるため、AND 条件下ではサブクエリ 2 は冗長となり、削除されます。

-- 後
SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c2 = 0);

OR 条件

-- 前
SELECT * FROM t1
WHERE EXISTS (SELECT 1 FROM t2 WHERE c2 = 0)   -- サブクエリ 1
   OR EXISTS (SELECT 1 FROM t2);               -- サブクエリ 2

サブクエリ 1 はサブクエリ 2 の部分集合であるため、OR 条件下ではより大きなセットが優先されるため、サブクエリ 1 が削除されます。

-- 後
SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2);

同一タイプのサブクエリのマージ

AND 条件

-- 前
SELECT * FROM t1
WHERE NOT EXISTS (SELECT t1.a AS f FROM t1 WHERE a > 10 AND b < 10)
  AND NOT EXISTS (SELECT a           FROM t1 WHERE a > 10 AND c < 3);

両方のサブクエリは同一テーブルを対象とし、共通の基本条件(a > 10)を持ちます。追加条件は OR で統合されます。

-- 後
SELECT * FROM t1
WHERE NOT EXISTS (SELECT t1.a AS f FROM t1 WHERE a > 10 AND (b < 10 OR c < 3));

OR 条件

-- 前
SELECT * FROM t1
WHERE EXISTS (SELECT t1.a AS f FROM t1 WHERE a > 10 AND b < 10)
   OR EXISTS (SELECT a           FROM t1 WHERE a > 10 AND c < 3);

両方のサブクエリは同一テーブルを対象とし、共通の基本条件を持ちます。追加条件は OR で統合されます。

-- 後
SELECT * FROM t1
WHERE EXISTS (SELECT t1.a AS f FROM t1 WHERE a > 10 AND (b < 10 OR c < 3));

相互に排他的なサブクエリの削除

EXISTS および NOT EXISTS — AND 条件

-- 前
SELECT * FROM t1
WHERE     EXISTS (SELECT 1 FROM t2 WHERE c1 = 0)   -- サブクエリ 1
  AND NOT EXISTS (SELECT 1 FROM t2);               -- サブクエリ 2

サブクエリ 1 はサブクエリ 2 の部分集合です。ある行が EXISTS (部分集合) および NOT EXISTS (上位集合) の両方を同時に満たすことは不可能であるため、AND 条件は常に偽になります。

-- 後
SELECT * FROM t1 WHERE false;

ANY および ALL の競合 — AND 条件

対象: > ANY< ALL または <= ALL< ANY> ALL または >= ALL

-- 前
SELECT * FROM t1
WHERE t1.c1 > ANY (SELECT c1 FROM t2 WHERE c1 > 10 AND c2 > 1)   -- ANY セット
  AND t1.c1 < ALL (SELECT c1 FROM t2 WHERE c1 > 10);              -- ALL セット

ANY セットは ALL セットの部分集合です。ある値が、部分集合の要素のいずれかより大きく、かつ上位集合のすべての要素より小さいという状態は成立しません → AND 条件は常に偽になります。

-- 後
SELECT * FROM t1 WHERE false; // ANY セットは ALL セットの部分集合です。

EXISTS および NOT EXISTS — OR 条件

-- 前
SELECT * FROM t1
WHERE     EXISTS (SELECT 1 FROM t2)                    -- サブクエリ 1
   OR NOT EXISTS (SELECT 1 FROM t2 WHERE c1 = 0);     -- サブクエリ 2

サブクエリ 2 はサブクエリ 1 の部分集合です。OR 条件下では、上位集合が空でない場合は条件が真になり、空の場合は部分集合の補集合が常に真になるため、OR 条件は常に真になります。

-- 後
SELECT * FROM t1 WHERE true; // サブクエリ 2 はサブクエリ 1 の部分集合です。

相互に排他的なサブクエリのマージ

EXISTS および NOT EXISTS のマージ

-- 前
SELECT * FROM t1
WHERE     EXIST (SELECT 1 FROM t2)                -- サブクエリ 1
  AND NOT EXIST (SELECT 1 FROM t2 WHERE c2 = 0); -- サブクエリ 2

NOT EXISTS セットは EXISTS セットの右部分集合です。オプティマイザーは両者を 1 回のスキャンに統合し、NOT EXISTS 述語に一致する行を除外するための HAVING 条件を追加します。

-- 後
SELECT * FROM t1
WHERE EXIST (
  SELECT 1 FROM t2
  HAVING SUM(CASE WHEN extra_cond THEN 1 ELSE 0 END) ==0
);
このマージは常に最適とは限りません。デフォルトでは、CBQT コンポーネントが適用可否を判断します。マージを強制するには、force_coalesce_subquery = ON を設定します。

次のチャートは、TPCH Q21 のクエリ実行時間(サブクエリフォールディング有効化前後)を示しています。バーが短いほどパフォーマンスが優れています。

image

IN および NOT IN(または = ANY および != ALL)のマージ

対象: IN + NOT IN(NOT IN セットが左部分集合);= ANY!= ALL(ALL セットが左部分集合)。

-- 前
SELECT * FROM t1
WHERE  t1.c1  = ANY (SELECT c1 FROM t2 WHERE c1 > 10)     -- = ANY セット
  AND t1.c1 != ALL  (SELECT c1 FROM t2 WHERE c1 > 100);   -- != ALL セット(左部分集合)

!= ALL セット(c1 > 100)は = ANY セット(c1 > 10)の部分集合です。オプティマイザーは、より大きなサブクエリに LNNVL 条件を追加して、より小さなサブクエリを満たす行を除外することで、これらをフォールドします。

-- 後
SELECT * FROM t1
WHERE t1.c1 = ANY (SELECT c1 FROM t2 WHERE c1 > 10 AND LNNVL(c1 > 100));

このマージは常に最適であり、force_coalesce_subquery の設定なしでデフォルトで適用されます。