SQL LEFT JOIN: 左側の全行を欠損込みで返す

SQL LEFT JOIN は左テーブルの全行を返し、右テーブルで一致した行を結合します。SQL の分類では SQL outer join の一種で、一致がない場合は右側の列が NULL で埋まります。関連データの有無にかかわらず片側の全レコードが必要な場面、たとえば注文有無を問わず顧客一覧を出す場面で使います。LEFT JOINLEFT OUTER JOIN は同義で、OUTER は省略可能です。

すべての行を返す SQL LEFT JOIN の例

出力:

ここに出力が表示されます...

出力:

+------+-------+
| name | total |
+------+-------+
| Alma | 79    |
| Alma | 150   |
| Ben  | NULL  |
| Cara | 45    |
+------+-------+
4 row(s)

この例の仕組み

  1. customers には 3 人、orders には 3 件の注文があり、customer_id で関連付けています。
  2. LEFT JOIN orders o ON o.customer_id = c.id により、各顧客に id が一致する注文行をすべて対応付けます。Alma には注文が 2 件あるため、結果も 2 行になります。
  3. Ben には注文がありません。LEFT JOIN は Ben の行を残し、o.total を NULL で埋めます(INNER JOIN ならこの行は消えます)。
  4. ORDER BY c.id, o.id で顧客単位にまとまった安定した並びになります。Ben の total が NULL なのは、金額 0 ではなく一致行が存在しないことを示します。

ON と WHERE: フィルタ位置が重要な理由

右テーブル条件を ON ではなく WHERE に置くのは、LEFT JOIN で最も多いミスです。気づかないうちに外部結合が内部結合相当へ変わります。

-- 誤り: o.total が NULL の Ben が WHERE で除外される
SELECT c.name, o.total
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.total > 50;

-- 正解: ON で結合時に絞るため、Ben は NULL のまま残る
SELECT c.name, o.total
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id AND o.total > 50;

ON 条件は結合処理中に評価されるため、左側で不一致の行は NULL 付きで残ります。WHERE は結合後に実行されるため、右側列が NULL の行を除外してしまい、LEFT JOIN が残したい行そのものを落とします。

Anti-Join: 一致しない行を見つける

LEFT JOIN と右テーブル列への IS NULL 判定を組み合わせると、関連行が存在しないレコードを抽出できます。

SELECT c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;

このクエリは注文が 0 件の Ben だけを返します。o.id が NULL になるのは左側不一致行だけだからです。NOT EXISTS でも同じ結果を得られ、出力に右テーブル列が不要な場合はそちらの方が意図を表しやすいことがあります。

LEFT JOIN と INNER JOIN の違い

LEFT JOININNER JOIN
左テーブルの全行を返し、右側不一致列は NULL両側で一致した行だけを返す
行数は左テーブル行数以上行数は小さい方の表の行数以下
不一致行にも意味がある用途(レポート、欠損確認)で使うすべての結果行で両側データが必要なときに使う

原則: 左側の全行を保持したいなら LEFT JOIN から始めます。不一致行が不要または不正データ扱いなら INNER JOIN を選びます。

SQL LEFT JOIN でよくある間違い

1 対多結合での行の増殖

LEFT JOIN は一致ペアごとに 1 行を返します。1 人の顧客に注文が 10 件あれば、その顧客は 10 行に増えます。これを考慮せずに集計すると合計や件数が膨らみます。

-- 誤り: 顧客数ではなく注文行数を数える
SELECT COUNT(*) FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id;

-- 正解: 顧客を重複排除して数える
SELECT COUNT(DISTINCT c.id) FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id;

JOIN で SELECT * を使う

JOIN クエリで SELECT * を使うと、c.ido.id のような同名列が重複して返り、アプリケーション側で列解決が曖昧になります。必要な列を明示して取得してください。

パフォーマンスの注意点

右テーブル側の結合キー(orders.customer_id)にインデックスを作ると、全表走査ではなく一致探索で結合できます。右側が大きく一部の行しか要らない場合は、サブクエリで事前に絞るか、条件を WHERE ではなく ON に置いて結合コストを下げつつ LEFT JOIN の意味を保ちます。

FAQ

LEFT JOIN と LEFT OUTER JOIN の違いは?

違いはありません。LEFT JOINLEFT OUTER JOIN は SQL 標準で同義です。PostgreSQL、MySQL、SQLite、SQL Server、Oracle など主要データベースは同じ意味で扱います。通常は短い LEFT JOIN が使われます。

WHERE 句を追加すると LEFT JOIN の行数が減るのはなぜですか?

右テーブル列に対する WHERE 条件は、NULL 拡張された行を除外します。その結果、LEFT JOIN が実質的に INNER JOIN のように振る舞います。不一致の左側行を残したい条件は ON 句に移してください。

INNER JOIN ではなく LEFT JOIN を使うべきタイミングは?

一致の有無にかかわらず左側行を必ず出力したいときです。完全性レポート、欠損データ検査、任意属性の付与などで有効です。一致行だけが有効という要件なら INNER JOIN を使います。