DevToolBox

SQL JOINの種類と使い分け

JOINは「2つの表をどう重ねるか」の指定です。種類はINNER/LEFT/RIGHT/FULL/CROSSの5つですが、 実務で迷うポイントは「残したい行はどちらの表か」「条件をONとWHEREのどちらに書くか」の2点に集約されます。

This guide explains SQL JOIN types with concrete result sets, and the classic pitfall where a WHERE condition silently turns a LEFT JOIN into an INNER JOIN.

TL;DR

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

以下の2表で全JOINの結果を比較します。

users(左表)orders(右表)
idnameuser_iditem
1佐藤1
2鈴木1ペン
3高橋4

2. 種類別の結果 / Result by JOIN type

JOIN結果(name, item)/ Result使いどころ / When to use
INNER JOIN佐藤-本, 佐藤-ペン両方に存在するデータだけ欲しい
LEFT JOIN佐藤-本, 佐藤-ペン, 鈴木-NULL, 高橋-NULL「注文の無いユーザー」も含めた一覧
RIGHT JOIN佐藤-本, 佐藤-ペン, NULL-机通常は表を入れ替えてLEFTで書く方が読みやすい
FULL OUTER JOIN上記すべて(鈴木-NULL, NULL-机 を含む)両側の突合・差分洗い出し(MySQL非対応)
CROSS JOIN3×3=9行の全組合せ組合せ生成。意図せず書くと行数爆発

3. 最大の罠: ONとWHEREの違い / The ON vs WHERE pitfall

「キャンセル以外の注文を、注文ゼロのユーザーも含めて一覧したい」とき、次のSQLは間違いです。

-- NG: 注文ゼロのユーザー(item IS NULL)がWHEREで消え、INNER JOINと同じ結果に
SELECT u.name, o.item
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE o.status <> 'cancelled';

-- OK: 右表への条件はONに書く
SELECT u.name, o.item
FROM users u
LEFT JOIN orders o ON o.user_id = u.id AND o.status <> 'cancelled';

ONは結合の最中に評価され、マッチしなくても左の行はNULL付きで残ります。 WHEREは結合の後に評価されるため、NULLになった行が条件で落ちます。 「LEFT JOINしたのに件数がINNERと同じ」ときはまずここを疑ってください。

ON filters while matching (unmatched left rows survive with NULLs); WHERE filters after the join (NULL rows get dropped). Put right-table conditions in ON to keep the LEFT JOIN semantics.

4. その他の頻出ポイント / Other common points

JOINが3つ以上連なるSQLは、インデントが揃っているだけで読み違いが激減します。SQL整形ツールに貼り付ければJOIN句単位で整形できます。

5. English summary

INNER JOIN keeps only matching rows; LEFT JOIN keeps every left row and fills missing right columns with NULL; RIGHT JOIN is the mirror (usually rewritten as LEFT for readability); FULL OUTER JOIN keeps both sides (not supported by MySQL — emulate with UNION); CROSS JOIN produces every combination. The classic bug: putting a right-table condition in WHERE after a LEFT JOIN filters out the NULL rows and silently degrades it to an INNER JOIN — put that condition in ON instead. With one-to-many keys the row count multiplies, so aggregate before joining when you need stable counts.

関連ツール / Related tools

関連ガイド / Related guides