MySQL 8.0 リファレンスマニュアル


8.2.1.10 外部結合の単純化

クエリーの FROM 句内のテーブル式は、多くの場合単純化されます。

パーサーステージでは、右外部結合操作を含むクエリーは、左結合操作のみを含む同等のクエリーに変換されます。 一般的に、変換は次の右結合になるように実行されます:

(T1, ...) RIGHT JOIN (T2, ...) ON P(T1, ..., T2, ...)

次の同等の左結合になります:

(T2, ...) LEFT JOIN (T1, ...) ON P(T1, ..., T2, ...)

形式 T1 INNER JOIN T2 ON P(T1,T2) のすべての内部結合式は、WHERE 条件に (または埋め込まれる結合の結合条件が存在する場合は、それに) 等位項として結合されるリスト T1,T2P(T1,T2) によって、置き換えられます。

オプティマイザが外部結合操作の計画を評価するときは、そのような操作ごとに外部テーブルが内部テーブルの前にアクセスされる計画のみが考慮されます。 このような計画でのみネステッドループアルゴリズムを使用して外部結合を実行できるため、オプティマイザの選択は制限されます。

次の形式のクエリーについて考えてみます。R(T2) では、テーブル T2 の一致する行数が大幅に絞り込まれます:

SELECT * T1 LEFT JOIN T2 ON P1(T1,T2)
  WHERE P(T1,T2) AND R(T2)

クエリーが書込み済として実行される場合、オプティマイザでは選択できませんが、より制限の少ないテーブル T1 にアクセスしてから、より限定されたテーブル T2 にアクセスすると、非常に非効率的な実行計画が生成される可能性があります。

かわりに、WHERE 条件が NULL 拒否の場合、MySQL はクエリーを外部結合操作なしのクエリーに変換します。 (つまり、外部結合を内部結合に変換します。) 条件は、その操作に対して生成された NULL で補完された行に対して FALSE または UNKNOWN と評価された場合、外部結合操作に対して NULL 拒否と呼ばれます。

したがって、この外部結合の場合:

T1 LEFT JOIN T2 ON T1.A=T2.A

これらのような条件は、NULL で補完された行 (T2 カラムが NULL に設定されている場合) では true にできないため、NULL 拒否されます:

T2.B IS NOT NULL
T2.B > 3
T2.C <= T1.C
T2.B < 2 OR T2.C > 1

次のような条件は、NULL で補完された行に対して true になる可能性があるため、NULL 拒否されません:

T2.B IS NULL
T1.B < 3 OR T2.B IS NOT NULL
T1.B < 3 OR T2.B > 3

外部結合操作で条件が NULL 拒否かどうかをチェックする一般的なルールは、次のとおりです:

  • A IS NOT NULL の形式で、A は内部テーブルのいずれかの属性です

  • いずれかの引数が NULL の場合に UNKNOWN に評価される内部テーブルへの参照を含む述語です

  • 結合として NULL 拒否条件を含む結合です

  • NULL 拒否条件の論理積です

条件は、クエリー内で、ある外部結合操作に対しては NULL を受け付けないが、ほかの外部結合操作に対しては NULL を受け付ける場合があります。 このクエリーでは、WHERE 条件は 2 番目の外部結合操作では NULL 拒否されますが、最初の外部結合操作では NULL 拒否されません:

SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A
                 LEFT JOIN T3 ON T3.B=T1.B
  WHERE T3.C > 0

WHERE 条件がクエリーの外部結合操作に対して NULL を受け付けない場合、外部結合操作は内部結合操作に置き換えられます。

たとえば、前述のクエリーでは、2 番目の外部結合は NULL 拒否され、内部結合で置換できます:

SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A
                 INNER JOIN T3 ON T3.B=T1.B
  WHERE T3.C > 0

元のクエリーの場合、オプティマイザは単一のテーブルアクセス順序 T1,T2,T3 と互換性のある計画のみを評価します。 リライトされたクエリーでは、アクセス順序 T3,T1,T2 も考慮されます。

ある外部結合操作の変換によって、別の操作の変換がトリガーされることがあります。 そのため、次のクエリー:

SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A
                 LEFT JOIN T3 ON T3.B=T2.B
  WHERE T3.C > 0

最初にクエリーに変換されます:

SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A
                 INNER JOIN T3 ON T3.B=T2.B
  WHERE T3.C > 0

これはクエリーと同等です:

SELECT * FROM (T1 LEFT JOIN T2 ON T2.A=T1.A), T3
  WHERE T3.C > 0 AND T3.B=T2.B

条件 T3.B=T2.B が NULL 拒否であるため、残りの外部結合操作を内部結合に置き換えることもできます。 これにより、外部結合のないクエリーが発生します:

SELECT * FROM (T1 INNER JOIN T2 ON T2.A=T1.A), T3
  WHERE T3.C > 0 AND T3.B=T2.B

オプティマイザが埋込み外部結合操作の置換に成功しても、埋込み外部結合を変換できない場合があります。 次のクエリー:

SELECT * FROM T1 LEFT JOIN
              (T2 LEFT JOIN T3 ON T3.B=T2.B)
              ON T2.A=T1.A
  WHERE T3.C > 0

次に変換されます:

SELECT * FROM T1 LEFT JOIN
              (T2 INNER JOIN T3 ON T3.B=T2.B)
              ON T2.A=T1.A
  WHERE T3.C > 0

それは埋め込む外部結合操作を含む形式にのみ書き換えることができます。

SELECT * FROM T1 LEFT JOIN
              (T2,T3)
              ON (T2.A=T1.A AND T3.B=T2.B)
  WHERE T3.C > 0

クエリーで埋込み外部結合操作を変換しようとする場合は、埋込み外部結合の結合条件を WHERE 条件とともに考慮する必要があります。 このクエリーでは、埋込み外部結合の WHERE 条件は NULL 拒否されませんが、埋込み外部結合 T2.A=T1.A AND T3.C=T1.C の結合条件は NULL 拒否されます:

SELECT * FROM T1 LEFT JOIN
              (T2 LEFT JOIN T3 ON T3.B=T2.B)
              ON T2.A=T1.A AND T3.C=T1.C
  WHERE T3.D > 0 OR T1.D > 0

したがって、クエリーは次のように変換できます:

SELECT * FROM T1 LEFT JOIN
              (T2, T3)
              ON T2.A=T1.A AND T3.C=T1.C AND T3.B=T2.B
  WHERE T3.D > 0 OR T1.D > 0

関連キーワード:  結合, 外部, クエリー, テーブル, 操作, WHERE, 条件, LEFT, インデックス, InnoDB