準備するテーブル
例として、Employees(従業員)テーブルと**Departments(部署)テーブル**を用意します。
- 従業員の中には、まだ部署に配属されていない人(田中さん)がいます。
- 部署の中には、まだ従業員が一人もいない部署(人事部)があります。
Employees テーブル (A)
| EmployeeID | EmployeeName | DepartmentID |
|---|---|---|
| 1 | 佐藤 | 101 |
| 2 | 鈴木 | 102 |
| 3 | 高橋 | 103 |
| 4 | 田中 | NULL |
Departments テーブル (B)
| DepartmentID | DepartmentName |
|---|---|
| 101 | 営業部 |
| 102 | 開発部 |
| 104 | 人事部 |
1. INNER JOIN (内部結合)
両方のテーブルに共通して存在するデータのみを結合します。
SQL
SELECT *
FROM Employees AS A
INNER JOIN Departments AS B ON A.DepartmentID = B.DepartmentID;
実行結果
| EmployeeID | EmployeeName | DepartmentID | DepartmentID | DepartmentName |
|---|---|---|---|---|
| 1 | 佐藤 | 101 | 101 | 営業部 |
| 2 | 鈴木 | 102 | 102 | 開発部 |
解説
EmployeesテーブルとDepartmentsテーブルの両方に存在するDepartmentID(101と102)を持つ行だけが表示されます。部署IDが103の高橋さん、NULLの田中さん、そして従業員がいない人事部(104)は結果に含まれません。
2. FULL OUTER JOIN (完全外部結合)
両方のテーブルのすべての行を結合します。片方にしか存在しないデータの場合、もう片方のカラムはNULLになります。
SQL
SELECT *
FROM Employees AS A
FULL OUTER JOIN Departments AS B ON A.DepartmentID = B.DepartmentID;
実行結果
| EmployeeID | EmployeeName | DepartmentID | DepartmentID | DepartmentName |
|---|---|---|---|---|
| 1 | 佐藤 | 101 | 101 | 営業部 |
| 2 | 鈴木 | 102 | 102 | 開発部 |
| 3 | 高橋 | 103 | NULL | NULL |
| 4 | 田中 | NULL | NULL | NULL |
| NULL | NULL | NULL | 104 | 人事部 |
解説
すべての従業員とすべての部署が表示されます。
- 高橋さん(
103)と田中さん(NULL)は、対応する部署がないため、部署名のカラムがNULLになります。 - 人事部(
104)は、対応する従業員がいないため、従業員情報のカラムがNULLになります。
3. FULL OUTER JOIN ... WHERE ... IS NULL (片方にしかない)
FULL OUTER JOINの結果から、両方に共通する部分(INNER JOINの結果)を除外します。どちらか一方のテーブルにしか存在しないデータのみを抽出します。
SQL
SELECT *
FROM Employees AS A
FULL OUTER JOIN Departments AS B ON A.DepartmentID = B.DepartmentID
WHERE A.EmployeeID IS NULL OR B.DepartmentID IS NULL;
実行結果
| EmployeeID | EmployeeName | DepartmentID | DepartmentID | DepartmentName |
|---|---|---|---|---|
| 3 | 高橋 | 103 | NULL | NULL |
| 4 | 田中 | NULL | NULL | NULL |
| NULL | NULL | NULL | 104 | 人事部 |
解説
部署が存在しない従業員(高橋さん、田中さん)と、従業員がいない部署(人事部)のみが表示されます。

4. LEFT JOIN (左外部結合)
左側のテーブル(Employees)のすべての行を基準に、右側のテーブル(Departments)を結合します。右側に一致するデータがない場合はNULLになります。
SQL
SELECT *
FROM Employees AS A
LEFT JOIN Departments AS B ON A.DepartmentID = B.DepartmentID;
実行結果
| EmployeeID | EmployeeName | DepartmentID | DepartmentID | DepartmentName |
|---|---|---|---|---|
| 1 | 佐藤 | 101 | 101 | 営業部 |
| 2 | 鈴木 | 102 | 102 | 開発部 |
| 3 | 高橋 | 103 | NULL | NULL |
| 4 | 田中 | NULL | NULL | NULL |
解説
Employeesテーブルの全従業員が表示されます。対応する部署がない高橋さんと田中さんは、部署関連のカラムがNULLになります。Departmentsテーブルにしか存在しない人事部(104)は結果に含まれません。
5. LEFT JOIN ... WHERE ... IS NULL (左側にしかない)
左側のテーブル(Employees)にのみ存在し、右側のテーブル(Departments)には対応するデータがない行を抽出します。
SQL
SELECT *
FROM Employees AS A
LEFT JOIN Departments AS B ON A.DepartmentID = B.DepartmentID
WHERE B.DepartmentID IS NULL;
実行結果
| EmployeeID | EmployeeName | DepartmentID | DepartmentID | DepartmentName |
|---|---|---|---|---|
| 3 | 高橋 | 103 | NULL | NULL |
| 4 | 田中 | NULL | NULL | NULL |
解説
部署に所属していない従業員(高橋さん、田中さん)だけが表示されます。
6. RIGHT JOIN (右外部結合)
右側のテーブル(Departments)のすべての行を基準に、左側のテーブル(Employees)を結合します。LEFT JOINの逆です。
SQL
SELECT *
FROM Employees AS A
RIGHT JOIN Departments AS B ON A.DepartmentID = B.DepartmentID;
実行結果
| EmployeeID | EmployeeName | DepartmentID | DepartmentID | DepartmentName |
|---|---|---|---|---|
| 1 | 佐藤 | 101 | 101 | 営業部 |
| 2 | 鈴木 | 102 | 102 | 開発部 |
| NULL | NULL | NULL | 104 | 人事部 |
解説
Departmentsテーブルの全部署が表示されます。対応する従業員がいない人事部(104)は、従業員情報のカラムがNULLになります。Employeesテーブルにしか存在しない従業員(高橋さん、田中さん)は結果に含まれません。
7. RIGHT JOIN ... WHERE ... IS NULL (右側にしかない)
右側のテーブル(Departments)にのみ存在し、左側のテーブル(Employees)には対応するデータがない行を抽出します。
SQL
SELECT *
FROM Employees AS A
RIGHT JOIN Departments AS B ON A.DepartmentID = B.DepartmentID
WHERE A.EmployeeID IS NULL;
実行結果
| EmployeeID | EmployeeName | DepartmentID | DepartmentID | DepartmentName |
|---|---|---|---|---|
| NULL | NULL | NULL | 104 | 人事部 |
解説
まだ従業員が一人も配属されていない部署(人事部)だけが表示されます。
実際に「へんてこ」な結合結果を見てみよう
2つのテーブルでLEFT JOINを実行すると、何が起こるか見てみましょう。
学生テーブル
| 学生ID | せいべつ |
|---|---|
| 1 | 0 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
部活テーブル
| 部活ID | せいべつ |
|---|---|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
クエリ
SELECT *
FROM 学生
LEFT JOIN 部活 ON 学生.せいべつ = 部活.せいべつ;
実行結果
| 学生ID | 学生.せいべつ | 部活ID | 部活.せいべつ |
|---|---|---|---|
| 1 | 0 | NULL | NULL |
| 2 | 1 | 1 | 1 |
| 2 | 1 | 2 | 1 |
| 2 | 1 | 3 | 1 |
| 2 | 1 | 4 | 1 |
| 3 | 1 | 1 | 1 |
| 3 | 1 | 2 | 1 |
| 3 | 1 | 3 | 1 |
| 3 | 1 | 4 | 1 |
| 4 | 1 | 1 | 1 |
| 4 | 1 | 2 | 1 |
| 4 | 1 | 3 | 1 |
| 4 | 1 | 4 | 1 |
何が起きたか
-
学生ID
1(せいべつ0) は、部活テーブルにマッチする行がないため、相手側がNULLの1行になります。これはLEFT JOINの正常な動きです。 -
学生ID
2(せいべつ1) は、部活テーブルの4つの行すべてにマッチします。そのため、学生ID2の行が4つに増殖してしまいました。 - 学生ID
3と4も同様に4つずつに増殖します。
結果として、元のテーブルは4行ずつだったのに、合計13行というとんでもない結果になってしまいました。これが「へんてこ」の正体です。
まとめ
- 文法上のルール: データ型が合っていれば、どんなカラムでも結合できる。
- 実用上のルール(ベストプラクティス): 意味のあるデータ関係を作るためには、**主キー(Primary Key)や外部キー(Foreign Key)**のような、一意性のある、あるいは親子関係を示すカラムで結合するのが普通です。
「一意性がないとダメな気がする」という感覚が、データ分析やシステム開発において、意図しない結果を避けるための非常に重要な感覚と言えます。





