VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
このガイドで使用するテーブル
結合列にインデックスを作成する
結合に使う列にインデックスがない場合、サーバーは一方の側にハッシュテーブルを 構築し、もう一方をスキャンします。EXPLAIN FORMAT=TREE がそれを示します。
join_buffer_size に収まらない場合、サーバーは
それをバッチ単位でディスクに退避します。インデックスを追加すると、そのすべてが
インデックス検索に置き換わります。
EXPLAIN 出力では、同じ 2 つのプランは Extra: Using join buffer (hash join) と type: ref として表示されます。この表には、サーバーが
選んだ結合の順序も示されます。これはクエリに 3 つ以上のテーブルが含まれると
すぐに重要になります。サーバーは上から下へテーブルを読み、最初のテーブルの
行数の見積もりが、その下のすべてに掛け合わされるためです。
プランの残りの部分については、EXPLAIN 出力の読み方で説明しています。
ハッシュ結合が検索できるのは等価条件です。代わりに範囲や不等式で結合すると、
サーバーはハッシュを構築しますが、検索に使えるものがありません。プランには
Inner hash join (no condition) と表示され、その上に条件を評価する Filter が
置かれるため、すべての行の組み合わせが生成されてから捨てられます。これが、
インデックス付きの等価結合が回避するコストです。
WHERE 句が LEFT JOIN を INNER JOIN に戻してしまう
最もよくある行数の誤りのバグであり、何も警告してくれません。o.amount が与えられます。
次に WHERE が各行を評価しますが、NULL > 50 は真ではなく NULL になるため、
それらの行は除外されます。条件を ON に移すと、代わりに何を一致とみなすかの
一部になります。
ルールはこうです。外部結合では、内側のテーブルに対する条件は ON に、保持
されるテーブルに対する条件は WHERE に置きます。例外は
WHERE <内側のテーブルのキー> IS NULL で、この場合は一致した行を除外すること
自体が目的です。
結合列の NULL は何にも一致しない
orders.customer_id は NULL を許容し、注文 4 には顧客がいません。内部結合は
この行を除外します。NULL は、別の NULL を含め、何とも等しくないためです。
customer_id が NULL だからで、これが人を驚かせる
部分です。行は存在し、金額は実在するのに、どの結合条件もそこに届きません。
この結合に基づいて作った売上合計は 15.00 足りず、何もそれを教えてくれません。
結合列が NULL を許容する場合は、結合を書く前に、孤立した行をどう扱うかを
決めてください。orders からの LEFT JOIN は、顧客名を NULL にしてその行を
保持します。背後にある比較のルールは、MySQL における NULLで説明しています。
一対多の結合は行を増やす
結合は一致ごとに 1 行を返すため、一致する行を複数持つテーブルと結合すると、 左側の行が一致の数だけ繰り返されます。その結果に対して計算した合計は誤りで、 たいていは大きすぎますが、クエリは正しく見えたままです。GROUP BY c.id と、
結合先の列に対する SUM または COUNT を使います。
MySQL の GROUP BY のパフォーマンスを参照してください。
1 つのクエリに 2 つの一対多結合があると、同じ計算がさらに早く狂います。2 組の
一致が互いに掛け合わされるためです。両方を一度に結合するのではなく、それぞれを
独自のサブクエリで集計してください。
ON がないとすべての組み合わせが返る
MySQL はON 句のない INNER JOIN を受け入れ、クロス結合として解釈します。
そのため、入力ミスはエラーではなく両テーブルの直積を生みます。10,000 行の
テーブル 2 つで 1 億行になります。
結合が不自然に切りの良い、不自然に大きな数を返したときは、何よりも先に ON の
欠落を疑ってください。FROM でテーブルをカンマで区切るのは古い構文での同じ
ものであり、一致条件はたいてい後方の WHERE にあります。
MySQL には FULL OUTER JOIN がない
MySQL にFULL OUTER JOIN はありません。2 つの部分を UNION で結合します。
UNION は重複行を取り除きます。これによって 2 つの部分がきれいにつながります。
UNION ALL は完全外部結合をまったく模倣しません。一致した各行を、それぞれの
部分から 1 回ずつ、計 2 回返します。保持しなければならない重複行がデータに
ある場合は、UNION ALL に頼るのではなく、キー列で重複を取り除いてください。
JOIN かサブクエリか
結合とIN サブクエリは同じフィルタを表現することが多く、オプティマイザが
どちらを好むかはデータ次第です。関連する行が存在するかどうかだけを知りたく、
その列は一切不要な場合、たいていは EXISTS のほうが読みやすく、サーバーは
最初の一致で停止できます。
MySQL におけるサブクエリと JOIN の比較では、
プランを使って両者を比較しています。
トラブルシューティング
関連項目
- EXPLAIN 出力の読み方 — 結合がどのインデックスを使ったかを教えてくれるプラン
- MySQL の外部キー — どの列にインデックスを作成するかを決める制約
- MySQL の GROUP BY のパフォーマンス — 一対多の結合を 1 行に集計し直す
- 結合の仕組み — まず基本から学びたい場合のチュートリアル