| この記事の要点 |
|
結論
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
子ページはありません
同階層のページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスのソフト一覧|Blender・Metasequoiaと用途別の選び方
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- Visual Studio「デザイナーを表示できません」エラーの原因と対処 2026-10-28 12:06:00
- SSH 認証に失敗しました。再試行してください の対処 2026-10-28 12:06:00
- SSH2 秘密鍵の読み込みに失敗しました の原因と対処 2026-10-28 12:06:00
- TeraTerm のよくあるエラーと対処法完全ガイド — Connection refused / 2026-10-28 12:06:00
- Visual Studio エラー一覧|コンパイルエラー・警告の確認方法と対処 2026-10-28 11:39:57
- Whitelabel Error Page の原因と消し方|Spring Boot のエラーページ 2026-10-28 11:39:57
- Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法 2026-10-28 11:39:57
- SQL WHERE 句完全ガイド 2026-10-28 11:39:57
- SQL JOIN 完全ガイド (INNER/LEFT/RIGHT/FULL OUTER) 2026-10-28 11:39:57
- tomcat の起動 / 停止ログと catalina.log・catalina.out の違い 2026-10-28 10:24:23
- Tomcat の起動確認方法 5 つ|localhost:8080・ポート・プロセス・ログ 2026-10-28 10:24:23
- Oracle 19c COMMITの使い方: 暗黙コミット・自動確定・応答不明時の確認 2026-10-28 09:59:09
- Oracle 19c DELETE文|削除対象の確認・COMMIT前の判断・ROLLBACKの条件 2026-10-28 09:59:09
- Oracle 19c INSERT文|INSERT ALL・SELECT・RETURNINGとMERGEの使い分け 2026-10-28 09:59:09
- Oracle 19cのトランザクション制御|COMMIT・ROLLBACK・分離レベルと接続設定 2026-10-28 09:59:09