結論:JOIN(内部結合/INNER JOIN)は「両方の表に一致する行だけ」を返し、LEFT JOIN(左外部結合)は「左の表の行は相手がいなくても全部残す」という違いです。 この一点さえ押さえれば、あとは実際にデータを結合してみて「行が消えるか、NULLで残るか」を目で確認するだけでほぼ理解できます。
そもそもなぜテーブルを結合する必要があるのか
SQLのテーブルは、わざと情報を分けて持たせてあることがよくあります。たとえば「注文(orders)」というテーブルには user_id という番号だけが入っていて、注文した人の名前そのものは入っていません。名前は「会員(users)」という別のテーブルにあります。
これは「1つの会員が何度も注文する」という1対多の関係を、無駄なく表現するための設計です。この考え方自体が曖昧だと JOIN も腑に落ちにくいので、先にそこから確認したい人は sql-dml-09: リレーションの種類 ― 1対多という関係 を見てみてください。
そして「orders の user_id と users の id が同じ行どうしを横につなげる」ための命令が JOIN です。
INNER JOIN(内部結合)とは何か
ただの JOIN は、正式には INNER JOIN(内部結合) と呼びます。書き方はこうです。
SELECT u.name, o.id, o.ordered_at
FROM orders AS o
JOIN users AS u ON o.user_id = u.id;
FROM のあとに結合したい表を書き、ON で「どの列とどの列が一致したらつなぐか」を指定します。ポイントは、INNER JOINは「両方の表で一致する行だけ」を返すということです。たとえば一度も注文していない会員がいた場合、その人はこの結果には出てきません。
この動きは実際に手を動かすと一番わかりやすいので、sql-dml-10: JOIN ― 2つの表を結びつける を開いて、初期表示のクエリをそのままブラウザで実行してみてください。登録不要・環境構築不要で、その場でSQLite上に用意された orders と users を結合した結果が返ってきます。
LEFT JOIN(外部結合)との違い
「一度も注文していない会員も含めて、会員全員を出したい」というときは INNER JOIN では実現できません。そこで使うのが LEFT JOIN(左外部結合)です。
SELECT u.name, o.id AS order_id
FROM users AS u
LEFT JOIN orders AS o ON o.user_id = u.id;
LEFT JOIN は「左に書いた表(FROM の直後)の行は全部残す。右の表に一致する行がなければ、その列は NULL になる」という結合です。つまり注文のない会員も、order_id が NULL の行として結果に出てきます。
JOIN(INNER JOIN)→ 一致する行だけ。相手がいない行は消えるLEFT JOIN→ 左の表は全部残る。相手がいなければNULL
この違いを言葉だけで覚えようとすると混乱しがちですが、sql-dml-12: LEFT JOIN ― 相手がいない行も残す では同じクエリを LEFT JOIN と JOIN で書き換えて実行し、行が消えるかどうかを見比べる演習が用意されています。「頭で理解する」より先に「目で見て納得する」ほうが、この違いは早く身につきます。
なお、「右の表を全部残す RIGHT JOIN」は、結合する2つの表の順番を入れ替えて LEFT JOIN で書き直せば同じ結果になります。実務でも LEFT JOIN だけで書く人が多いので、まずは JOIN と LEFT JOIN の2つの違いを確実に理解しておけば十分です。
3つ以上の表をつなぐ・多対多になる場合
JOIN は2つの表だけでなく、3つ以上の表を連鎖させてつなぐこともできます。「注文」「会員」「商品」のように関係する表が増えても、JOIN を重ねて書けば1つの結果にまとめられます。この書き方は sql-dml-11: JOIN の連鎖 ― 3つ以上の表をつなぐ で練習できます。
また「1人の会員が複数の商品を買い、1つの商品も複数の会員に買われる」ような**多対多(N:M)**の関係は、中間テーブルを挟んで JOIN する必要があります。この考え方は sql-dml-13: 多対多(N:M)と中間テーブル で扱っています。
つまずきやすいところ
JOIN を書き始めたころによく出会うエラーがあります。
usersとordersの両方にidのような同じ名前の列があり、SELECT idのようにテーブル名を省略して書いてしまう → err-sql-04: ambiguous column name ― 列名があいまい。u.idのようにテーブル名.列名の形で書けば解決します。ONで指定した列名やエイリアスを打ち間違える → err-sql-01: no such column ― 列が見つからないJOINの構文自体が崩れている(ONの書き忘れなど)→ err-sql-03: syntax error ― 構文の崩れ
これらは JOIN を使い始めた人がほぼ必ず一度は通る道です。エラーメッセージが出たら焦らず、「どの列がどちらの表のものか」を1つずつ確認する癖をつけると解決が早くなります。他のエラーも含めて SQLのエラー逆引き に12種類まとまっているので、詰まったときの辞書として使ってください。
SELECT・WHEREの基礎がまだの人へ
JOIN の前に SELECT と WHERE の基本がまだ不安な場合は、先に SQL SELECTの書き方|WHEREで条件を絞る使い方を初心者向けに解説 を読んでおくと、JOIN の説明にすんなり入れます。
次に読む
- SQL文法コース(データを取り出す) — SELECT・WHERE・集計・JOINまでブラウザで実行しながら学べる
- sql-dml-09: リレーションの種類 ― 1対多という関係 — なぜテーブルを分けるのかの前提
- sql-dml-10: JOIN ― 2つの表を結びつける — INNER JOINの基本
- sql-dml-12: LEFT JOIN ― 相手がいない行も残す — 外部結合との違い
- sql-dml-11: JOIN の連鎖 ― 3つ以上の表をつなぐ — 表が増えたとき
- sql-dml-13: 多対多(N:M)と中間テーブル — 中間テーブルを挟む結合
- SQLのエラー逆引き — つまずいたときの辞書代わりに