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

PolarDB:サブクエリの最適化

最終更新日:Dec 14, 2025

SQL 文内の冗長または事前計算可能なサブクエリは、クエリ分析と実行のオーバーヘッドを増加させ、データベースのパフォーマンスを低下させます。この問題は、オブジェクトリレーショナルマッピング (ORM) フレームワークが複雑なネストされたクエリを自動生成する場合によく発生します。PolarDB for MySQL は、オプティマイザーステージで SQL 文を再書き込みするサブクエリ最適化機能を提供します。不要なサブクエリを自動的に削除し、定数サブクエリを事前計算することで、実行計画を簡素化し、クエリパフォーマンスを大幅に向上させます。

適用範囲

  • プロダクトシリーズ:Cluster Edition および Standard Edition

  • カーネルバージョン:MySQL 8.0.2、リビジョン 8.0.2.2.19 以降。

サブクエリ最適化の有効化

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

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

  • PolarDB コンソール

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

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

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

    • 操作手順SET コマンドを使用してデータベースセッションでパラメーターを変更する場合は、loose_ プレフィックスを削除し、元のパラメーター名を使用してください。

パラメーター

レベル

説明

loose_simplify_subq_mode

グローバル/セッション

この機能のメインスイッチです。有効な値は次のとおりです:

  • REPLICA_ON (デフォルト):読み取り専用 (RO) ノードでのみこの機能を有効にします。

  • ON:この機能を有効にします。

  • OFF:この機能を無効にします。

最適化シナリオと例

シナリオ 1:冗長な SELECT ネストの排除

サブクエリが集計関数や式をラップしているだけで、SELECT (SELECT SUM(a) FROM t2) FROM dual のように他の複雑なロジックがない場合、オプティマイザーは外側の SELECT を削除し、コア式を直接実行します。

-- 射影列内
SELECT (SELECT SUM(a) FROM t2) FROM dual;
-- 最適化後
SELECT SUM(`test`.`t2`.`a`) AS `sum(a)` FROM `test`.`t2`

-- HAVING 句内
SELECT SUM(a) FROM t2 HAVING (SELECT(SELECT(SELECT count(b))));
-- 最適化後
SELECT SUM(`testdb`.`t2`.`a`) AS `SUM(a)` from `testdb`.`t2` HAVING (0 <> count(`testdb`.`t2`.`b`))
  1. テストデータの準備

    DROP TABLE IF EXISTS t2;
    CREATE TABLE t2 (
        id INT PRIMARY KEY AUTO_INCREMENT,
        a INT,
        b INT
    );
    INSERT INTO t2 (a, b) VALUES (10, 100), (20, NULL), (50, 200), (120, NULL);
  2. 最適化を無効にしてクエリを実行:まず、セッションでサブクエリ最適化機能を無効にして、最適化前の実行計画を表示します。

    SET simplify_subq_mode = 'OFF';
    EXPLAIN SELECT SUM(a) FROM t2 HAVING (SELECT(SELECT(SELECT count(b))));
    SHOW warnings;
    
    -- 結果
    /* select#1 */ select sum(`testdb`.`t2`.`a`) AS `SUM(a)` from `testdb`.`t2` having (0 <> (/* select#2 */ select (/* select#3 */ select (/* select#4 */ select count(`testdb`.`t2`.`b`)))))
  3. 最適化を有効にしてクエリを実行:次に、セッションでサブクエリ最適化機能を有効にして、同じクエリを実行します。

    SET simplify_subq_mode = 'ON';
    EXPLAIN SELECT SUM(a) FROM t2 HAVING (SELECT(SELECT(SELECT count(b))));
    SHOW warnings;
    
    -- 結果
    /* select#1 */ select sum(`testdb`.`t2`.`a`) AS `SUM(a)` from `testdb`.`t2` having (0 <> count(`testdb`.`t2`.`b`))

シナリオ 2:[NOT] EXISTS サブクエリの事前評価

オプティマイザーが [NOT] EXISTS サブクエリが常に true (空でない結果) または false (空の結果) と評価されると判断した場合、[NOT] EXISTS 句を TRUE または FALSE に置き換えます。この置き換えにより、サブクエリの実行が回避されます。

-- 空でないセット
SELECT * FROM t1 WHERE EXISTS(SELECT MAX(a) FROM t2);
-- 最適化後
SELECT * FROM t1

-- 空のセット、WHERE/HAVING 句=false、または LIMIT 0
SELECT * FROM t1 WHERE EXISTS(SELECT max(a) FROM t2 HAVING 1=2 );
-- 最適化後
SELECT * FROM t1 WHERE false
  1. テストデータの準備

    DROP TABLE IF EXISTS t1;
    DROP TABLE IF EXISTS t2;
    CREATE TABLE t1 (id INT);
    CREATE TABLE t2 (val INT);
    INSERT INTO t1 VALUES (1), (2);
  2. 最適化を無効にしてクエリを実行:まず、セッションでサブクエリ最適化機能を無効にして、最適化前の実行計画を表示します。

    SET simplify_subq_mode = 'OFF';
    EXPLAIN SELECT * FROM t1 WHERE EXISTS(SELECT MAX(a) FROM t2);
    SHOW warnings;
    
    -- 結果
    /* select#1 */ select `testdb`.`t1`.`id` AS `id` from `testdb`.`t1` where exists(/* select#2 */ select max(`testdb`.`t1`.`id`) from `testdb`.`t2`)
  3. 最適化を有効にしてクエリを実行:次に、セッションでサブクエリ最適化機能を有効にして、同じクエリを実行します。

    SET simplify_subq_mode = 'ON';
    EXPLAIN SELECT * FROM t1 WHERE EXISTS(SELECT MAX(a) FROM t2);
    SHOW warnings;
    
    -- 結果
    /* select#1 */ select `testdb`.`t1`.`id` AS `id` from `testdb`.`t1`

シナリオ 3:ANY/ALL サブクエリ内の定数射影への LIMIT 1 の追加

ANY または ALL 句のサブクエリが定数のみをクエリし、列を一切含まない場合、複数の同一行を取得する必要はありません。オプティマイザーは、これらのサブクエリに自動的に LIMIT 1 を追加し、サブクエリ内のテーブルに対する不要な全表スキャンを回避します。

-- 最適化前
SELECT * FROM t1 WHERE a > ANY (SELECT 1 FROM t2);
-- 最適化後
SELECT * FROM t1 WHERE a > ANY (SELECT 1 FROM t2 LIMIT 1);

本番環境での推奨事項

  • 十分なテスト:ほとんどの場合、この最適化は論理的な等価性を保証しますが、特定の実行順序やサブクエリの実行回数に依存するまれなシナリオではリスクが存在します。本番環境のコアビジネス運用でこの機能を使用する前に、ステージング環境で有効にし、完全な回帰テストを実行してください。

  • 統計情報の定期的な更新:空または空でないセットの推論など、一部の最適化はテーブル統計に依存します。統計情報が古いと、オプティマイザーが次善の選択をする可能性があります。これを防ぐために、テーブルに対して定期的に ANALYZE TABLE を実行してください。