| この記事の要点 |
|
結論
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 / Maya)
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- プロジェクトをTomcatプロジェクトとして認識させる方法 2026-10-07 22:32:50
- MySQLの1366 Incorrect string value|Laravelの文字コード・絵文字エラー 2026-10-07 21:54:03
- curlの証明書ホスト名不一致|旧エラー51・現行60の確認と対処 2026-10-07 21:54:03
- LaravelのMassAssignmentException|fillableの原因と安全な対処 2026-10-07 21:54:03
- Eclipse で Tomcat の起動ログがコンソールに出ない時の確認手順 2026-10-07 21:54:02
- MySQLにおける中央値(Median)の導き方(バージョン8未満) 2026-10-07 13:49:45
- getInputForward 2026-10-07 13:41:15
- JSONから配列に変換 2026-10-07 13:41:15
- ビューから値をモデルに格納しコントローラーで受け取る方法 2026-10-07 13:23:41
- Laravelのテーブル作成と定義変更|マイグレーション・up/down・注意点 2026-10-07 13:23:41
- NumPy 配列に要素を追加する方法 (append / concatenate) 2026-10-07 13:23:41
- MariaDB・MySQLで現在日時を取得する方法|NOW・タイムゾーン・保存型 2026-10-07 13:13:36
- 【django】テンプレートで定数を使用する方法 2026-10-07 13:10:15
- Spring BootにおけるApplication.propertiesの環境依存設定の分割方法 2026-10-07 12:09:35
- Not supported for DML operations【Springエラー】 2026-10-07 11:09:38