クエリーの FROM
節内テーブル表現は多くの場合単純化されています。
パーサー段階で、右外側 join オペレーションを含むクエリーは左 join オペレーションを含む等価のクエリーに変換されます。一般的には、変換は次の規則に従って実行されます。
(T1, ...) RIGHT JOIN (T2,...) ON P(T1,...,T2,...) = (T2, ...) LEFT JOIN (T1,...) ON P(T1,...,T2,...)
T1 INNER JOIN T2 ON P(T1,T2)
フォームのすべての内側 join 表現は
T1,T2
、P(T1,T2)
結合された WHERE
条件によって置き換えられます。(あるいは、組み込まれた
join の join
条件が存在する場合は、それに置き換えられます)。
オプティマイザが外側 join オペレーションの join クエリー計画を評価する際、各オペレーション時、外側テーブルが内側テーブルより前にアクセスされます。そのようなプランは、入れ子ループスキーマによる外側 join オペレーションを含むクエリーの実行のみ可能なため、オプティマイザ選択肢は制限されています。
たとえば、次のようなフォームのクエリーがあるとします。
SELECT * T1 LEFT JOIN T2 ON P1(T1,T2) WHERE P(T1,T2) AND R(T2)
R(T2)
がテーブル
T2
からマッチする行を大幅に狭めます。クエリーをそのまま実行した場合、オプティマイザは
T1
より前に
T2
をアクセスする以外に選択肢が与えられず、非常に非効率的な実行プランになることがあります。
幸い、MySQL WHERE
条件が null-rejected
の場合、そのようなクエリーを外側 join
オペレーションを含まないクエリーに変換します。もし外側
join オペレーションが
NULL
-によって補われたオペレーションのために作成された行の
FALSE
または
UNKNOWN
に評価される場合は、null-rejected
と呼ばれます。
よって、この外側 join では:
T1 LEFT JOIN T2 ON T1.A=T2.A
次のような条件は null-rejected です:
T2.B IS NOT NULL, T2.B > 3, T2.C <= T1.C, T2.B < 2 OR T2.C > 1
次のような条件は null-rejected ではありません:
T2.B IS NULL, T1.B < 3 OR T2.B IS NOT NULL, T1.B < 3 OR T2.B > 3
条件が外側 join オペレーションにとって null-rejected か否かを確認する一般的な規則は単純です。次の場合、条件は null-rejected になります。
フォームが A IS NOT
NULL
で、A
が内側テーブルのどれかの属性である場合
引数の 1 つが NULL
のときに UNKNOWN
と評価される内側テーブルへの参照を含む述語である場合
null-rejected 条件をコンジャンクトとして含んでいる結合子の場合。
null-rejected 条件のディスジャンクションの場合。
条件は 1 つクエリーでの外側 join オペレーションで null-rejected となり、ほかのクエリーで not null-rejected になりえます。次のクエリーでは:
SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A LEFT JOIN T3 ON T3.B=T1.B WHERE T3.C > 0
WHERE
条件は、2
つめの外側 join オペレーションでは null-rejected
であり、1 つめの外側 join オペレーションでは
not null-rejected です。
もし WHERE
条件がクエリーの外側 join オペレーションで
null-rejected である場合、外側 join
オペレーションは内側 join
オペレーションに置き換えられます。
たとえば、前のクエリーは次のクエリーに置き換えられます。
SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A INNER JOIN T3 ON T3.B=T1.B WHERE T3.C > 0
オリジナルクエリーには、1 つのアクセス順序
T1,T2,T3
と対応するプランをオプティマイザが評価します。クエリーを置き換える場合は、T3,T1,T2
アクセスシーケンスを追加で考慮します。
1 つの外側 join オペレーションの変換は別のオペレーションの変換を引き起こす場合があります。よって、次のクエリーでは:
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-rejected
であることと、外側 join
を含まないクエリーを取得したため、残る外側
join オペレーションは内側 join
に置き換えることができます。
SELECT * FROM (T1 INNER JOIN T2 ON T2.A=T1.A), T3 WHERE T3.C > 0 AND T3.B=T2.B
時折、組み込まれた外側 join オペレーションを置き換えることができても、組み込まれた外側 join が変換できない場合があります。次のクエリーでは:
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,
それは組み込まれた外側 join オペレーションを含むフォームにのみ書き換えることができます。
SELECT * FROM T1 LEFT JOIN (T2,T3) ON (T2.A=T1.A AND T3.B=T2.B) WHERE T3.C > 0.
組み込まれた外側 join
オペレーションをクエリーに変換する場合、WHERE
条件とともに組み込まれた外側 join の join
条件を考慮しなければいけません。次のクエリーでは:
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
WHERE
は組み込まれた外側 join では not null-rejected
ですが、組み込まれた外側
joinT2.A=T1.A AND T3.C=T1.C
の join 条件は null-rejected
になります。よって、クエリーは次に変換されます。
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