基本 ローカル(VS Code + Java) 約20分

JOIN でテーブルをつなぐ基本

INNER JOIN と LEFT JOIN を使い、複数テーブルのデータを結び付けて読む基本を学ぶレッスン。

前回までに CRUD(SELECT / INSERT / UPDATE / DELETE)の基本を学びました。今回は、複数のテーブルをつなげてデータを取得する JOIN を扱います。

引き続き DB Browser for SQLite(practice.db)を使います。

なぜテーブルを分けるのか

これまでの employees テーブルでは、部署名を文字列でそのまま入れていました。

idnamedepartmentage
1田中太郎開発部28
2鈴木花子営業部32
3佐藤次郎開発部25

この方法には問題があります。

  • 「開発部」の名前を「開発1部」に変えたい場合、全行を UPDATE しなければならない
  • 入力ミスで「開発部」と「開発 部」(スペース入り)が混ざると、別の部署として扱われる

解決策は、部署の情報を別のテーブルに分けることです。employees テーブルには部署の ID だけを持たせ、部署名は departments テーブルで管理します。

2つのテーブルの関係を図にします。

erDiagram
    departments {
        INTEGER id PK
        TEXT name
    }
    employees {
        INTEGER id PK
        TEXT name
        INTEGER department_id FK
        INTEGER age
    }
    departments ||--o{ employees : "1つの部署に複数の社員"

2つのテーブルを準備する

まず既存の employees テーブルを削除して、新しい構造で作り直します。

DROP TABLE IF EXISTS employees;

部署テーブルを作ります。

CREATE TABLE departments (
  id INTEGER PRIMARY KEY,
  name TEXT
);

INSERT INTO departments (id, name) VALUES (1, '開発部');
INSERT INTO departments (id, name) VALUES (2, '営業部');
INSERT INTO departments (id, name) VALUES (3, '総務部');

社員テーブルを作ります。department 列のかわりに department_id 列を持たせます。

CREATE TABLE employees (
  id INTEGER PRIMARY KEY,
  name TEXT,
  department_id INTEGER,
  age INTEGER
);

INSERT INTO employees (id, name, department_id, age) VALUES (1, '田中太郎', 1, 28);
INSERT INTO employees (id, name, department_id, age) VALUES (2, '鈴木花子', 2, 32);
INSERT INTO employees (id, name, department_id, age) VALUES (3, '佐藤次郎', 1, 25);
INSERT INTO employees (id, name, department_id, age) VALUES (4, '山田美咲', 3, 29);
INSERT INTO employees (id, name, department_id, age) VALUES (5, '高橋健一', 2, 35);

department_id1 は departments テーブルの id = 1(開発部)に対応します。このように、別テーブルの行を指し示す列を外部キーと呼びます。

INNER JOIN でテーブルをつなぐ

employees テーブルだけを SELECT すると、department_id は数字のままです。

SELECT * FROM employees;
idnamedepartment_idage
1田中太郎128
2鈴木花子232

部署名を表示したいときは、departments テーブルと結合します。

SELECT employees.name, departments.name, employees.age
FROM employees
INNER JOIN departments ON employees.department_id = departments.id;
employees.namedepartments.nameemployees.age
田中太郎開発部28
鈴木花子営業部32
佐藤次郎開発部25
山田美咲総務部29
高橋健一営業部35

SQL の構造を分解します。

  • FROM employees — ベースのテーブルを指定
  • INNER JOIN departments — 結合するテーブルを指定
  • ON employees.department_id = departments.id — 結合条件(「employees の department_id と departments の id が一致する行をつなげる」)

テーブル名.列名 の形で、どちらのテーブルの列かを明示します。

Java で同じことをすると

Java で2つの ArrayList を結合するイメージです。

for (Employee e : employees) {
    for (Department d : departments) {
        if (e.departmentId == d.id) {
            System.out.println(e.name + " " + d.name);
        }
    }
}

二重ループと条件一致で探す処理を、SQL では JOIN と ON の1文で書けます。

別名をつけて短く書く

テーブル名が長いと SQL も長くなります。AS で別名をつけると短く書けます。

SELECT e.name, d.name, e.age
FROM employees AS e
INNER JOIN departments AS d ON e.department_id = d.id;

e が employees、d が departments の略です。結果は同じですが、読みやすくなります。

WHERE と組み合わせる

JOIN した結果をさらに絞り込むこともできます。

SELECT e.name, d.name, e.age
FROM employees AS e
INNER JOIN departments AS d ON e.department_id = d.id
WHERE d.name = '開発部';
e.named.namee.age
田中太郎開発部28
佐藤次郎開発部25

JOIN でテーブルを結合 → WHERE で絞り込み、という順番です。

LEFT JOIN — 一致しない行も表示する

もう1人、部署が決まっていない社員を追加します。

INSERT INTO employees (id, name, department_id, age) VALUES (6, '中村亮', NULL, 27);

department_idNULL(値なし)です。INNER JOIN では NULL は一致条件を満たさないため、この社員は結果に含まれません。

SELECT e.name, d.name
FROM employees AS e
INNER JOIN departments AS d ON e.department_id = d.id;

中村亮は表示されません。

LEFT JOIN を使うと、左側のテーブル(employees)の全行が表示されます。

SELECT e.name, d.name
FROM employees AS e
LEFT JOIN departments AS d ON e.department_id = d.id;
e.named.name
田中太郎開発部
鈴木花子営業部
佐藤次郎開発部
山田美咲総務部
高橋健一営業部
中村亮(NULL)

中村亮の部署名は NULL になりますが、行自体は表示されます。「一致しないものも含めて全件見たい」ときに LEFT JOIN を使います。

ここまでのまとめ

JOIN の種類動作
INNER JOIN両テーブルで条件が一致する行だけ返す
LEFT JOIN左テーブルの全行を返す(右が NULL でも含む)
概念意味
外部キー別テーブルの行を指す列(department_id → departments.id)
ON 条件結合するための一致条件
ASテーブルや列に別名をつける

DB コースのまとめと次へ

ここまでで学んだ SQL の範囲をまとめます。

内容扱った SQL
DB の基本概念と環境導入
データの取得・絞り込み・並び替えSELECT / WHERE / ORDER BY
データの追加・更新・削除INSERT / UPDATE / DELETE
テーブルの結合と外部キーJOIN / ON

次の回へ

INNER JOIN と LEFT JOIN の結果の違いを DB Browser で確認できたら、このコースは完了です。次は HTTP / REST / JSON の最小マップ で、ブラウザとサーバーのやり取りを短く整理します。そのあと Spring Boot を使い、ここまで学んだ SQL が Spring Data JPA の CRUD 操作とどう対応しているかを見ていきます。