JOIN句とWHERE句、条件を書く場所で結果が変わる理由
SQLでJOINを書くとき、「条件はON句とWHERE句のどちらに置くのか」「sql join where 順番はどうなるのか」は よくある疑問です。INNER JOINでは同じ結果になる条件でも、LEFT JOINでは置き場所ひとつで残るはずの左側の行が消えることがあります。実務で事故になりやすい違いを具体例で確認します。
This guide explains when ON and WHERE produce identical results, and why clause placement can silently change the meaning of a LEFT JOIN.
TL;DR
- INNER JOIN: 追加条件をONとWHEREのどちらに書いても論理的な結果は同じ
- LEFT JOIN: 右表の条件をWHEREに書くとNULL行が消え、INNER JOIN相当になり得る
- 左表を必ず全件残したいなら、右表への絞り込み条件はONに書く
1. サンプルデータ / Sample data
顧客3人と注文3件で、「発送済み(shipped)の注文だけを結合する」ケースを比較します。
| customers(左表) | orders(右表) | |||
|---|---|---|---|---|
| id | name | customer_id | item | status |
| 1 | 佐藤 | 1 | 本 | shipped |
| 2 | 鈴木 | 1 | ペン | cancelled |
| 3 | 高橋 | 2 | 机 | cancelled |
高橋には注文がなく、鈴木には注文があるものの発送済みの注文はありません。
2. INNER JOINではONとWHEREの結果は同じ / INNER JOIN equivalence
INNER JOINは一致した行だけを残します。そのため、追加条件としてo.status = 'shipped'をON句に書く場合と、結合後にWHERE句へ書く場合は論理的に同じ結果です。
-- ONで条件を追加
SELECT c.name, o.item
FROM customers c
INNER JOIN orders o
ON o.customer_id = c.id
AND o.status = 'shipped';
-- WHEREで条件を追加(結果は同じ)
SELECT c.name, o.item
FROM customers c
INNER JOIN orders o ON o.customer_id = c.id
WHERE o.status = 'shipped';| name | item |
|---|---|
| 佐藤 | 本 |
どちらも佐藤だけが残ります。可読性のため、表の対応条件はON、結果全体の絞り込みはWHERE、と意図で分けるのが一般的です。
3. LEFT JOINでは結果が変わる / The LEFT JOIN pitfall
「全顧客を残し、発送済み注文があれば表示する」なら、右側のordersへの条件はON句に書きます。 JOIN時に条件を満たす行だけが結合され、満たさない場合も左側の行は残り、右側だけNULLで埋まります。
-- OK: 左側の全顧客を残す
SELECT c.name, o.item
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.id
AND o.status = 'shipped';| name | item |
|---|---|
| 佐藤 | 本 |
| 鈴木 | NULL |
| 高橋 | NULL |
同じ条件をWHEREへ移すと、JOINの後に結果全体がフィルタされます。一致しなかった高橋も、発送済み注文がない鈴木もo.status = 'shipped'を満たさず除外されます。
-- NG: NULLの行がWHEREで消える
SELECT c.name, o.item
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.status = 'shipped';| name | item |
|---|---|
| 佐藤 | 本 |
この例では結果的にINNER JOINと同じ挙動です。「LEFT JOINと書けば左側は全件残る」とは限りません。 なお、WHERE条件がIS NULLのようにNULLを残す条件なら挙動は別です。
A right-table predicate in ON limits matches while preserving every left row. The same predicate in WHERE rejects NULL-extended rows after the join, making this LEFT JOIN behave like an INNER JOIN.
4. なぜ違うのか: SQLの論理的な処理順序 / Logical query order
「sql join where どっちが先か」を理解する鍵は、記述順ではなく結果を定める論理的な処理順序です。 標準SQLの意味論は、おおむね次の順で考えられます。
FROM → JOIN → ON → WHERE → GROUP BY → HAVING → SELECT → ORDER BYONは結合候補の一致を決め、OUTER JOINは一致しない側をNULLで補います。その後、WHEREができあがった行をフィルタするため、 右表の値をWHEREで要求するとNULL補完行まで落ちます。
これは論理的な意味論です。オプティマイザは同じ結果を保てる範囲で条件評価やJOINを並べ替えるため、 物理的な実行計画が必ずこの順に逐次処理するわけではありません。
5. 「JOINの前にWHEREで絞る」はどう書くか / Filter before joining
「sql join の前に whereを実行したい」「sql join 前に絞る」と考えても、SQL本文でWHEREをJOINより前には書けません。 右表の結合対象を限定するならON句へ条件を追加するか、複雑な前処理なら派生テーブルやCTEで先に絞ります。
-- ONで右表の結合対象を限定
LEFT JOIN orders o
ON o.customer_id = c.id
AND o.status = 'shipped'
-- 複雑な前処理ならCTEで明示
WITH shipped_orders AS (
SELECT customer_id, item FROM orders WHERE status = 'shipped'
)
SELECT c.name, o.item
FROM customers c
LEFT JOIN shipped_orders o ON o.customer_id = c.id;ただし、先に記述した処理が物理的にも先に実行されるとは限りません。実際の方法はオプティマイザが決定します。
6. ONとWHEREでパフォーマンスは変わるか / Performance
「sql join where パフォーマンス」が気になる場合、まず結果が論理的に同値かを確認します。現代のRDBMSでは、 INNER JOINの同値なON条件とWHERE条件を同じ実行計画へ変換することが多く、性能に大差が付かないのが一般的です。
ただしDBMS、統計情報、インデックス、クエリの複雑さによっては異なる場合があります。LEFT JOINのように結果自体が変わる 書き換えは単なる性能比較ではありません。実データに近い環境でEXPLAINやEXPLAIN ANALYZEを使い、実行計画と実測値を確認するのが確実です。
7. 実務での判断基準 / Practical rule
- 結合キー: テーブル同士の対応関係なのでON句に書く
- INNER JOINの追加条件: 結合条件ならON、結果全体の条件ならWHEREと意図で分ける
- LEFT JOINの右表条件: 左側を全件残したいなら必ずON句に書く
- LEFT JOIN後のWHERE: 左表の条件、またはNULL行を消す意図が明確な条件だけを書く
結論は、「LEFT JOINの左側は必ず全件残したい」なら右側テーブルへの絞り込み条件はON句に書くことです。 長いSQLはSQL整形ツールでJOINと条件の対応を見やすくすると、レビュー時の見落としも減らせます。
8. English summary
With an INNER JOIN, moving an additional predicate between ON and WHERE produces the same logical result because only matched rows survive. With a LEFT JOIN, a right-table predicate in ON restricts matches but preserves all left rows with NULLs, while the same predicate in WHERE removes those NULL-extended rows. SQL's logical order is FROM, JOIN, ON, WHERE, GROUP BY, HAVING, SELECT, then ORDER BY; this describes semantics, not necessarily physical execution. Equivalent INNER JOIN forms usually receive the same plan, but verify important queries with EXPLAIN. If every left row must remain, put right-table filters in ON.
関連ツール / Related tools
- SQL Formatter - JOIN句とON・WHERE条件を読みやすく整形