DevToolBox

JOIN句とWHERE句、条件を書く場所で結果が変わる理由

最終更新日: 2026-07-22公開日: 2026-07-22執筆: DevToolBox編集部

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

1. サンプルデータ / Sample data

顧客3人と注文3件で、「発送済み(shipped)の注文だけを結合する」ケースを比較します。

customers(左表)orders(右表)
idnamecustomer_iditemstatus
1佐藤1shipped
2鈴木1ペンcancelled
3高橋2cancelled

高橋には注文がなく、鈴木には注文があるものの発送済みの注文はありません。

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';
nameitem
佐藤

どちらも佐藤だけが残ります。可読性のため、表の対応条件は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';
nameitem
佐藤
鈴木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';
nameitem
佐藤

この例では結果的に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 BY

ONは結合候補の一致を決め、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のように結果自体が変わる 書き換えは単なる性能比較ではありません。実データに近い環境でEXPLAINEXPLAIN ANALYZEを使い、実行計画と実測値を確認するのが確実です。

7. 実務での判断基準 / Practical rule

結論は、「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

関連ガイド / Related guides