テーブル結合の基礎(INNER JOIN/LEFT JOIN)
概要
- 日程: Day 2 / セッション 3
- 時間: 10:25-10:55
- 形式: 座学
- ゴール: INNER JOIN と LEFT JOIN の違いを「マッチしない行をどう扱うか」で説明し、適切に使い分けられる
- 学習形式: 対話型解説
導入(5分)
これまでテーブルを1つずつ眺めてきました。employees を見れば誰が誰か分かる。departments を見れば部署名が分かる。でも、「山田太郎さんの部署名は?」と聞かれて、皆さんは答えられるでしょうか。
employees テーブルには department_id しかありません。departments テーブルには department_id と department_name があります。この2つをつなげて初めて「山田太郎 → 営業部」と分かるのです。
このつなぎ役が、今日の主役 JOIN(ジョイン) です。
本編(20分)
1. なぜテーブルは分かれているのか
そもそも、社員データに最初から「営業部」と書いておけばよかったのでは? という疑問が出ます。
答え:情報の重複を避けるためです。
たとえば営業部が「東京」から「横浜」に移転したとしましょう。社員テーブルに location まで持たせていたら、営業部の全員のレコードを書き換える必要があります。100人いれば100行。1人忘れたら「東京勤務になってる営業部員」が爆誕します。
部署情報は departments に1行だけ持ち、社員側は department_id で参照する——これがリレーショナルデータベースの基本思想です。図書館で本ごとに著者の住所を書いておかず、「著者名簿」を別に持つようなものです。
2. INNER JOIN — 両方に存在するものだけ
コード例・実例
山田太郎さんの部署名を取り出すSQL:
SELECT e.first_name, e.last_name, d.department_name
FROM employees AS e
INNER JOIN departments AS d
ON e.department_id = d.department_id
WHERE e.employee_id = 1;
結果:
| first_name | last_name | department_name |
|---|---|---|
| 太郎 | 山田 | 営業部 |
書き方の構造:
SELECT 列リスト
FROM 左テーブル
INNER JOIN 右テーブル
ON 結合条件
WHERE ...;
ここがポイント
ONには「どの列同士で対応づけるか」を書く。多くは外部キー=主キーの組み合わせAS eは エイリアス(あだ名)。employees.first_nameをe.first_nameと短く書けるINNER JOINは「両方のテーブルに該当する行だけ」を返す
department_idあり"] --|INNER JOIN ON e.dept_id = d.dept_id|--> C["結合結果
両方に存在する組合せ"] B["departments
department_id"] --> C
3. INNER JOIN の落とし穴
両方に存在しないとどうなるか、考えてみましょう。
たとえば、新人さんがまだ部署未配属で department_id が NULL だとします。すると、その新人さんは INNER JOIN の結果から消えます。「該当する部署が見つからない」とみなされるためです。
これは、いわばお見合いパーティーで「両方が手を挙げた組み合わせだけ成立」ルールです。片方の手が挙がっていないと、マッチング表に名前が出てきません。
4. LEFT JOIN — 左側を全部残す
「未配属の新人さんも含めて、社員全員と所属部署を一覧したい」——そんなときが LEFT JOIN の出番です。
コード例・実例
SELECT e.first_name, e.last_name, d.department_name
FROM employees AS e
LEFT JOIN departments AS d
ON e.department_id = d.department_id;
FROM の側(左テーブル)の行は全部残り、右側にマッチしなかった部分は NULL になります。
| first_name | last_name | department_name |
|---|---|---|
| 太郎 | 山田 | 営業部 |
| 花子 | 鈴木 | 開発部 |
| ... | ... | ... |
| 新人さん | 未配属 | NULL |
ここがポイント
- LEFT JOIN は「左側のテーブルを基準に、右側で見つかれば付ける」
- 見つからなければ右側の列は NULL
- 「全社員を漏れなく出したい」「未紐付けも確認したい」シーンで使う
全員"] --|LEFT JOIN|--> C["結合結果
社員は全員残る・
部署なしは NULL"] B["departments"] --> C
コラム — JOINで件数が増えるミステリー
JOIN初心者が必ず一度はぶつかる謎が「件数が想定より多い」現象です。たとえば顧客と注文をJOINすると、1顧客に複数注文があるとその顧客の名前が注文の数だけ現れます。「あれ、株式会社ABCが5社あるぞ……?」と慌てる前に、これは仕様です。1対多のテーブルを結合すれば「多」の側の件数になります。慣れるとむしろこれが嬉しい(明細表が作れる)ようになります。
5. RIGHT JOIN と FULL JOIN(紹介程度)
- RIGHT JOIN:LEFT JOIN の左右が逆。右側のテーブルを基準にする
- FULL JOIN:左右どちらに偏らず、どちらかに存在すれば全部出す(マッチしない側はNULL)
実務ではほぼ INNER と LEFT で足りるので、まずはこの2つを完璧に。RIGHT は「LEFTで書き換えれば同じ」ことから、書く人もまれです。FULL は「両側の差分を見たい」ような特殊な場面で使います。
6. 自己結合(チラ見せ)
同じテーブル同士をJOINすることもできます。たとえば「上司と部下」を1つの employees テーブルで管理しているとき、自分自身ともう一度JOINすることで「上司の名前」を出せます。これを自己結合と呼びます。詳しくは演習で必要になったら触れます。
💬 AIに聞いてみよう
- 「INNER JOIN と WHERE で書く昔ながらの書き方(カンマ区切り)の違いは?」
- 「LEFT JOIN と LEFT OUTER JOIN って同じ?」
- 「JOIN を3つ以上つなげるとき、どんな順に書くのが読みやすい?」
まとめ(5分)
JOINは「複数テーブルをONで指定した条件で結びつける」操作です。違いを1行でまとめると:
- INNER JOIN:両方に存在する組み合わせだけ
- LEFT JOIN:左側は全部残す。右側になければ NULL
「漏らしたくない側を左に置いて LEFT JOIN」と覚えると、現場で迷いません。
次のセッションでは、employees × departments と customers × orders × order_items × products の2系統を実際にJOINします。INNERとLEFTで結果がどう変わるかを目で確認しましょう。
🔄 振り返りチェック
- INNER JOIN と LEFT JOIN の違いを、ベン図のイメージで説明できますか?
- なぜ部署情報を employees に直接持たず、別テーブルにするのでしょうか?
- 「未配属の新人を含めた全社員一覧」を出すには、どちらのJOINを使いますか?
補足資料
- 参考リンク: PostgreSQL公式: JOIN
- 発展課題: 自分の身の回り(推し活、ゲーム、学校生活など)から「2つの表をJOINしたくなる例」を3つ考えてみる
学習ガイド
想定される質問と回答例
| 質問 | ヒント |
|---|---|
| エイリアス(AS e)は必須? | 必須ではないが、JOINでは事実上必須レベル。employees.first_name を毎回書くのは大変、列名が両テーブルに存在する場合は曖昧さ回避にも必要 |
| ON と WHERE の違いは? | ON は「どう結合するか」、WHERE は「結合後に絞る条件」。書き分けないと LEFT JOIN の意味が変わってしまうことがある |
| JOINの結果は元のテーブルより行が多くなる?少なくなる? | 場合による。1対1なら同じ、1対多なら多の方に合わせて増える、INNER で片方しかなければ減る。「件数の見積もり」が大事 |
| INNER JOIN を書かずに WHERE で同じことができる? | 古い書き方では FROM employees e, departments d WHERE e.department_id = d.department_id でほぼ同じ。ただし LEFT JOIN 相当は書けない、可読性も劣る |
つまずきやすいポイント
| つまずきポイント | ヒント |
|---|---|
| ON 条件を書き忘れ/書き間違える | 全行同士の組み合わせ(直積)が出てしまう(10×5=50行など)。「想定より極端に多い」ときは ON 句を最初に疑う |
| INNERのつもりが LEFT、または逆 | 「漏らしたくない方を左に置く」と覚える。employees LEFT JOIN departments なら社員側を漏らさない |
| エイリアスを定義したのに元のテーブル名で書いて怒られる | AS e を付けたら、以降は e.列名 で書く。employees.列名 はエラーになる |
| LEFT JOIN したのに右側で WHERE 条件を書いて INNER 化 | LEFT JOIN の後で WHERE d.location = '東京' のように右側を絞ると、NULL の行が消えて事実上 INNER と同じになる。フィルタは ON 句に書くのが正解 |