JOINの概念:集合論としての理解
MySQLをはじめとするリレーショナルデータベースにおいて、JOIN(結合)は最も頻繁に使用される操作の一つです。JOINを正しく理解することは、効率的なクエリ作成だけでなく、データの整合性を保つ上でも非常に重要です。
概念的に言えば、JOINは数学の「集合論」における積集合(インターセクション)や和集合(ユニオン)の考え方に似ています。例えば、集合A {1, 2, 3} と集合B {2, 3, 4} がある場合、両者に共通する要素を見つけ出したり、一方の要素をベースにもう一方を紐付けたりする作業がJOINにあたります。
検証用データの準備
ここでは、部署(departments)と従業員(staffs)という2つのエンティティを例に説明します。
1. 部署テーブル (departments)
CREATE TABLE departments (
dept_id VARCHAR(5) PRIMARY KEY,
dept_name VARCHAR(20)
);
INSERT INTO departments VALUES ('D01', '開発部'), ('D02', '営業部');
2. 従業員テーブル (staffs)
CREATE TABLE staffs (
staff_name VARCHAR(20),
assigned_dept VARCHAR(5)
);
INSERT INTO staffs VALUES
('佐藤', 'D01'),
('田中', 'D02'),
('鈴木', 'D03'), -- 存在しない部署
('高橋', 'D01');
JOINの種類と挙動
1. CROSS JOIN(クロス結合)
CROSS JOINは「デカルト積」とも呼ばれ、左テーブルの各行に対して右テーブルのすべての行を組み合わせます。ON句による結合条件を指定しない場合にこの結果になります。
SELECT d.dept_name, s.staff_name
FROM departments d
CROSS JOIN staffs s;
このクエリの結果、2(部署数)× 4(従業員数)= 8行のデータが生成されます。基本的には意図的に全組み合わせが必要な場合以外は使用しません。
2. INNER JOIN(内部結合)
INNER JOINは、指定した条件(ON句)に一致する行のみを両方のテーブルから抽出します。共通項のみを抽出する「積集合」のイメージです。
SELECT d.dept_id, d.dept_name, s.staff_name
FROM departments d
INNER JOIN staffs s ON d.dept_id = s.assigned_dept;
この場合、部署D03に所属する「鈴木」さんは、departmentsテーブルに一致するIDがないため、結果には含まれません。
3. LEFT JOIN(左外部結合)
LEFT JOINは、左側のテーブル(FROM句のテーブル)のすべての行を表示し、右側のテーブルに一致するデータがあればそれを結合します。一致しない場合は、右側の列はNULLになります。
SELECT s.staff_name, d.dept_name
FROM staffs s
LEFT JOIN departments d ON s.assigned_dept = d.dept_id;
この結果、「鈴木」さんの行も表示されますが、dept_nameはNULLとなります。これは、マスタデータが欠落しているデータのチェックなどにも有効です。
応用:不一致データの抽出
特定の条件に該当しない(補集合にあたる)データを抽出したい場合、LEFT JOINとWHERE句を組み合わせます。例えば、「部署に所属していない(または存在しない部署IDを持っている)従業員」を探すクエリは以下のようになります。
SELECT s.staff_name
FROM staffs s
LEFT JOIN departments d ON s.assigned_dept = d.dept_id
WHERE d.dept_id IS NULL;
テーブル間の多重度(カーディナリティ)
JOINを設計する際、テーブル間の関係性を把握することが不可欠です。
- 1対1: ユーザーとプロフィール詳細など。データの分割が目的。
- 多対1(1対多): 従業員と部署など。最も一般的なJOINのケース。
- 多対多: 学生と講義など。この場合、通常は中間テーブルを介して「多対1」の関係を2つ作ります。
特に注意すべきは「多対多」の関係を誤って直接結合することです。これにより、意図せず行数が倍増し(データ爆発)、集計結果が不正になるリスクがあります。