クエリの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
が内側テーブルのどれかの属性である場合
UNKNOWN
のアーギュメントの内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