タイトル: ONとWHEREの違い
SEOタイトル: SQL JOIN の ON と WHERE の違い 完全ガイド(内部結合 / 外部結合 / NULL の扱い / フィルタ順序)
| この記事の要点 |
|
結論
SQL で複数のテーブルを結合する際、ON と WHERE はどちらも条件を書けますが、役割が違います。
- ON — どの行同士を組み合わせるか(結合条件)
- WHERE — 結合結果のうちどれを残すか(フィルタ条件)
INNER JOIN ではどちらに書いても結果が同じになるケースが多いですが、LEFT / RIGHT / FULL OUTER JOIN では結果が変わります。
サンプルデータ
顧客テーブル customers と注文テーブル orders を例に説明します。
-- customers
id | name
---+-------
1 | Alice
2 | Bob
3 | Carol (注文なし)
-- orders
id | customer_id | amount | status
---+-------------+--------+--------
10 | 1 | 1000 | paid
11 | 1 | 500 | canceled
12 | 2 | 2000 | paid
INNER JOIN の場合(ほぼ同じ結果)
-- 書き方 A: ON に条件を書く
SELECT c.name, o.amount
FROM customers c
JOIN orders o
ON o.customer_id = c.id
AND o.status = 'paid';
-- 書き方 B: WHERE に条件を書く
SELECT c.name, o.amount
FROM customers c
JOIN orders o
ON o.customer_id = c.id
WHERE o.status = 'paid';
INNER JOIN ではどちらも結果は同じです。
| name | amount |
|---|---|
| Alice | 1000 |
| Bob | 2000 |
LEFT JOIN の場合(結果が違う)
ここが ON / WHERE の本質的な違いが出る場面です。
パターン A: ON に書く(LEFT JOIN として正しく動く)
SELECT c.name, o.amount
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.id
AND o.status = 'paid';
| name | amount |
|---|---|
| Alice | 1000 |
| Bob | 2000 |
| Carol | NULL |
Carol は注文がないため、orders 側は NULL のまま残る。これが LEFT JOIN らしい挙動です。
パターン B: WHERE に書く(実質 INNER JOIN になる)
SELECT c.name, o.amount
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.id
WHERE o.status = 'paid';
| name | amount |
|---|---|
| Alice | 1000 |
| Bob | 2000 |
Carol は o.status が NULL となり、NULL = 'paid' は真にならないため WHERE で消されます。結果として LEFT JOIN を書いたのに INNER JOIN と同じ結果になる、という典型的な落とし穴です。
実行順序のイメージ
SQL は概念的に次の順序で評価されます。
- FROM — テーブルを並べる
- ON — 結合条件で行を組み合わせる
- JOIN タイプ — LEFT / RIGHT なら不一致側に NULL 行を追加
- WHERE — 結合結果をフィルタ
- GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
つまり、WHERE はすでに NULL 補完が済んだ結果に対して動くので、外側テーブルの条件を WHERE に書くと NULL 行を切り落としてしまうわけです。
書き分けの指針
| 条件の性質 | 書く場所 | 例 |
|---|---|---|
| 結合キー | ON | o.customer_id = c.id |
| 結合先テーブル自身の絞り込み(OUTER JOIN) | ON | AND o.status = 'paid' |
| 結果セット全体の絞り込み | WHERE | WHERE c.country = 'JP' |
| NULL を残したい条件 | ON | 同上 |
| NULL を残してはいけない条件 | WHERE | 意図的に INNER 化したいとき |
INNER JOIN なら気にしなくていい?
結果は同じになりますが、意図を読む人に伝えるため、結合キー / 結合先絞り込みは ON に、結果全体のフィルタは WHERE に分けて書くのが推奨されます。後から OUTER JOIN に変更しても破綻しにくくなります。
OUTER JOIN で WHERE を使いたいケース
「LEFT JOIN したうえで、マッチしなかった行だけを抽出したい」場合は WHERE に NULL 判定を書きます。
-- 注文が 1 件もない顧客を取得
SELECT c.name
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.id
WHERE o.id IS NULL;
FAQ
Q: ON に常に 1=1 と書くとどうなる?
A: 全行同士をマッチさせるクロス結合と同等です。デバッグ用途以外には使いません。
Q: 古い書き方 FROM a, b WHERE a.id = b.id はどう違う?
A: 旧来の「カンマ結合」では結合条件もすべて WHERE に書きます。OUTER JOIN は表現できず、SQL-92 の JOIN ... ON 書式が現代の標準です。
Q: 集計関数の条件は WHERE?
A: 集計後の条件は HAVING に書きます(例: HAVING SUM(amount) > 1000)。
関連
- RDBMS共通 — 親カテゴリ
- INNER JOIN / LEFT JOIN / RIGHT JOIN / FULL OUTER JOIN
- WHERE 句 / HAVING 句
- NULL の評価 / IS NULL / COALESCE
- サブクエリと EXISTS