MySQL 8.0 リファレンスマニュアル MySQL NDB Cluster 8.0 を含む

このページは機械翻訳したものです。

8.2.2.1 準結合変換による IN および EXISTS サブクエリー述語の最適化

準結合は、テーブルのプルアウト、重複の除去、最初の一致、緩やかなスキャン、実体化などの複数の実行戦略を可能にする準備時変換です。 オプティマイザは、このセクションで説明するように、準結合戦略を使用してサブクエリーの実行を改善します。

2 つのテーブル間の内部結合の場合、結合は、他方のテーブルに一致がある回数だけ、一方のテーブルから 1 行を返します。 ただし、問題によっては、重要な情報は一致の数ではなく、一致があるかどうかだけの場合があります。 コースカリキュラムのクラスとクラス名簿 (各クラスに登録されている生徒) をそれぞれ一覧表示する classroster というテーブルがあるとします。 実際に生徒が登録されているクラスを一覧表示するには、次の結合を使用できます。

SELECT class.class_num, class.class_name
    FROM class
    INNER JOIN roster
    WHERE class.class_num = roster.class_num;

ただし、結果には、登録された生徒ごとに、各クラスが 1 回ずつ一覧表示されます。 ここでの問題では、これは不要な情報の重複です。

class_numclass テーブルの主キーであると仮定すると、SELECT DISTINCT を使用して重複抑制が可能ですが、後で重複を排除するためにのみ、最初に一致するすべての行を生成することは非効率的です。

同じ重複のない結果は、次のサブクエリーを使用して取得できます。

SELECT class_num, class_name
    FROM class
    WHERE class_num IN
        (SELECT class_num FROM roster);

ここで、オプティマイザは IN 句に roster テーブルから各クラス番号のインスタンスを 1 つだけ返すサブクエリーが必要であることを認識できます。 この場合、クエリーでは準結合を使用できます。つまり、roster の行と一致する class の各行のインスタンスを 1 つのみ返す操作です。

EXISTS サブクエリー述語を含む次のステートメントは、IN サブクエリー述語を含む前述のステートメントと同等です:

SELECT class_num, class_name
    FROM class
    WHERE EXISTS
        (SELECT * FROM roster WHERE class.class_num = roster.class_num);

MySQL 8.0.16 以降では、EXISTS サブクエリー述語を含むステートメントは、同等の IN サブクエリー述語を含むステートメントと同じ準結合変換の対象となります。

MySQL 8.0.17 以降、次のサブクエリーはアンチ結合に変換されます:

つまり、IN (SELECT ... FROM ...) または EXISTS (SELECT ... FROM ...) 形式のサブクエリーの否定は、アンチ結合に変換されます。

アンチ結合は、一致がない行のみを返す操作です。 次に示すクエリーについて考えてみます:

SELECT class_num, class_name
    FROM class
    WHERE class_num NOT IN
        (SELECT class_num FROM roster);

このクエリーは内部的にアンチ結合 SELECT class_num, class_name FROM class ANTIJOIN roster ON class_num としてリライトされ、roster のどの行とも一致しない class の各行のインスタンスが返されます。 つまり、class の各行については、roster で一致が見つかるとすぐに class の行を破棄できます。

比較対象の式が NULL 値可能な場合、ほとんどの場合、アンチ結合変換は適用できません。 このルールの例外は、(... NOT IN (SELECT ...)) IS NOT FALSE とそれに相当する (... IN (SELECT ...)) IS NOT TRUE をアンチ結合に変換できることです。

外部結合および内部結合の構文は外部クエリー仕様で許可され、テーブル参照は実テーブル、導出テーブル、ビュー参照または共通テーブル式です。

MySQL では、サブクエリーを準結合として処理するには、次の基準を満たす必要があります (MySQL 8.0.17 以降では、NOT がサブクエリーを変更する場合はアンチ結合):

サブクエリーが前述の基準を満たす場合、MySQL は準結合 (MySQL 8.0.17 以降ではアンチ結合) に変換し、次の方法からコストベースの選択を行います:

これらの各戦略は、次の optimizer_switch システム変数フラグを使用して有効または無効にできます:

これらのフラグはデフォルトで有効になっています。 セクション8.9.2「切り替え可能な最適化」を参照してください。

オプティマイザは、ビューおよび導出テーブルの処理の違いを最小限に抑えます。 これは、STRAIGHT_JOIN 修飾子を使用するクエリーおよび準結合に変換可能な IN サブクエリーを含むビューに影響します。 次のクエリーは、処理の変更によって変換が変更されるため、実行計画が異なるため、これを示しています:

CREATE VIEW v AS
SELECT *
FROM t1
WHERE a IN (SELECT b
           FROM t2);

SELECT STRAIGHT_JOIN *
FROM t3 JOIN v ON t3.x = v.a;

オプティマイザはまずビューを参照し、IN サブクエリーを準結合に変換してから、ビューを外部クエリーにマージできるかどうかをチェックします。 外部クエリーの STRAIGHT_JOIN 修飾子によって準結合が妨げられるため、オプティマイザはマージを拒否し、実体化テーブルを使用して導出テーブルを評価します。

EXPLAIN 出力には、次のような準結合戦略の使用が示されています:

MySQL 8.0.21 以降では、準結合変換は、単一テーブル UPDATE、または[NOT] IN または[NOT] EXISTS サブクエリー述語を使用する DELETE ステートメントへ適用もできます。ただし、ステートメントが ORDER BY または LIMIT を使用せず、準結合変換がオプティマイザヒントにより、または optimizer_switch 設定により許可されている場合です。