JOIN演習(顧客と注文をつなぐ)
概要
- 日程: Day 2 / セッション 4
- 時間: 10:55-11:40
- 形式: 実習
- ゴール: employees×departments、customers×orders×order_items×products を結合する SQL を 3 本書け、INNER と LEFT の結果差を説明できる
- 学習形式: ハンズオン実習(AIペアプログラミング)
導入(5分)
前のセッションで「テーブルをつなぐとはどういうことか」を頭で理解しました。今度は、自分の手で実際にJOINを書いて、画面に出てくる結果を見て、「あ、本当に名前と部署名が一緒に出てきた!」という感動を味わってもらいます。
特に今日のハイライトは、customers → orders → order_items → products という4テーブルJOIN。これができると、「3月に株式会社ABCが何をいくつ買ったか」みたいな業務でよくある問いに、SQL一発で答えられるようになります。
本編(10分)
1. JOINで使うエイリアスの設計
複数テーブルをJOINするときは、最初に短いエイリアスを決めると書きやすくなります。
| テーブル名 | エイリアス例 |
|---|---|
| employees | e |
| departments | d |
| customers | c |
| orders | o |
| order_items | oi |
| products | p |
| categories | cat |
エイリアスを付けたら、以降の列指定は e.first_name のように書きます。これで「どのテーブルの first_name か」が一目瞭然です。
2. 4テーブルJOINの考え方
「customers が何をいくつ買ったか」を出すには、4つのテーブルを下図のようにつなぎます。
c"] --|c.customer_id = o.customer_id|--> O["orders
o"] O --|o.order_id = oi.order_id|--> OI["order_items
oi"] OI --|oi.product_id = p.product_id|--> P["products
p"]
書く順番は「左から右に流れる」ようにすると読みやすい。JOIN は何個でもつなげられますが、ON句を間違えると結果が爆発するので、一つずつ追加して確認するのがコツです。
💬 AIに聞いてみよう
- 「3テーブル以上のJOINで、ON句の書き方のコツは?」
- 「JOINした結果の件数が想定と違うとき、どう調査すればいい?」
- 「
SELECT *で全列出すと、JOIN後はどう表示される?」
実習・演習(30分)
課題
課題1: employees × departments で「誰がどこの部署か」一覧
すべての社員と所属部署名を、社員ID順に表示してください。
期待する列:employee_id, first_name, last_name, department_name
-- ヒント: INNER JOIN
SELECT e.employee_id, 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
ORDER BY e.employee_id;
実行したら、件数を確認してください(10件のはず)。
課題2: INNER と LEFT の結果差を体感する
次のSQLをそれぞれ実行し、件数を比較してください。
-- INNER JOIN
SELECT d.department_name, e.first_name
FROM departments AS d
INNER JOIN employees AS e
ON d.department_id = e.department_id;
-- LEFT JOIN(departments側を基準に)
SELECT d.department_name, e.first_name
FROM departments AS d
LEFT JOIN employees AS e
ON d.department_id = e.department_id;
差を観察するために、次のSQLでテストデータを足してから再実行してみてください(BEGIN/ROLLBACK で囲んで安全に):
BEGIN;
INSERT INTO departments (department_name, location) VALUES ('総務部', '東京');
-- INNERとLEFTを再実行
-- ...
ROLLBACK;
期待する観察:
- INNER:「総務部」が出てこない(社員0人なので消える)
- LEFT:「総務部」が出る、ただし first_name は NULL
課題3: customers × orders で「誰がいつ、いくら注文したか」
注文日順に、顧客名・注文日・合計金額を表示してください。
SELECT c.customer_name, o.order_date, o.total_amount
FROM customers AS c
INNER JOIN orders AS o
ON c.customer_id = o.customer_id
ORDER BY o.order_date;
実行後、「8人の顧客に対して10件の注文がある」「同じ顧客が複数回出てくる」ことを確認してください。
課題4: 4テーブルJOIN — 「誰が何をいくつ買ったか」
customers × orders × order_items × products を結合し、注文ごとの明細を表示してください。
期待する列:customer_name, order_date, product_name, quantity, unit_price
SELECT c.customer_name,
o.order_date,
p.product_name,
oi.quantity,
oi.unit_price
FROM customers AS c
INNER JOIN orders AS o ON c.customer_id = o.customer_id
INNER JOIN order_items AS oi ON o.order_id = oi.order_id
INNER JOIN products AS p ON oi.product_id = p.product_id
ORDER BY o.order_date, c.customer_name;
課題5(チャレンジ): 集計と組み合わせる
課題4を発展させ、顧客ごとの購入金額合計を出してください。
- 列:
customer_name, 合計金額 - 合計金額は
SUM(oi.quantity * oi.unit_price)で計算 GROUP BY c.customer_name、ORDER BY 合計金額 DESCで並べる
成果物
以下を1ファイルにまとめてください。
- 課題1〜4の実行SQLと結果(先頭の5行程度でOK)
- 課題2でINNERとLEFTの件数差が何件だったか、その理由
- 課題5の結果と、トップ3顧客の名前
ヒント
- JOINが多くなったら、まず2テーブルだけで成立させて、1つずつ足していく
- 結果の件数が想定より多い/少ないときは、まずJOINを1段階ずつ外して確認
- AIに「このSQLが何をしているか日本語で説明して」と頼むと、自分の理解確認に使える
まとめ(5分)
JOINで世界が広がったはずです。1つのテーブルでは答えられなかった「誰が、何を、いつ、いくつ買ったか」がワンクエリで取れる——これは現場で本当によく使うパターンです。
特に大事なポイント:
- エイリアスを使う(短く、テーブル頭文字が定番)
- 1つずつJOINを追加して件数を確認する
- INNERとLEFTの違いはマッチしない行の扱い
次のセッションでは、JOINの結果に必ず登場する「NULL」と、条件分岐の CASE 式を学びます。たとえばLEFT JOINで出てきた NULL を「未配属」と表示するなど、結果を見やすく整える技です。
🔄 振り返りチェック
- 4テーブルJOINを、何も見ずに ON句までスラスラ書けますか?
- INNER と LEFT で件数差が出る場面の具体例を1つ言えますか?
- JOIN後の件数が「想定より多い」とき、原因をどう調べますか?
補足資料
- 参考リンク: PostgreSQL公式: SELECT構文
- 発展課題: 「3月の注文だけを対象に、商品カテゴリ別の売上ランキング」を出すSQLを書いてみる(categoriesテーブルも加えた5テーブルJOIN)
学習ガイド
想定される質問と回答例
| 質問 | ヒント |
|---|---|
| 同じ顧客名が複数行に出るのはなぜ? | 1顧客×複数注文だから。明細表としては正常。集計したいなら GROUP BY を使う |
| エイリアスは何文字でもいい? | 何文字でもOKだが、慣習的には1〜3文字。テーブル頭文字(e, d, c, o)がよく使われる |
| JOINの順番を変えても結果は同じ? | INNER JOINだけなら結果は同じ(順番は実行計画の話)。LEFT JOIN を混ぜると順番で意味が変わる |
| SELECT * でJOINすると同じ名前の列はどうなる? | 両方とも出力される。表示上は同じ列名で並ぶが、内部的には別物。明示的に列を選ぶのが安全 |
つまずきやすいポイント
| つまずきポイント | ヒント |
|---|---|
| ON 句で = の片側にエイリアスを書き忘れる | ON department_id = department_id だとどちらの department_id か曖昧でエラー。ON e.department_id = d.department_id と必ず両側に |
| 件数が想定より多い(行が増える) | 1対多のJOINでは「多」の数だけ増える。集計したいときは GROUP BY、明細を出したいなら受け入れる |
| LEFT JOIN したのに NULL の行が消える | 右側テーブルの列を WHERE で絞ると、その NULL が落ちる。フィルタは ON句に入れる |
| エイリアスを使い始めた途端、列が見つからないエラー | エイリアスを定義した後は、元のテーブル名は使えない。employees.列名 → e.列名 に統一 |