SQL結合はリレーショナルデータベースの基盤です。関連する列に基づいて、2つ以上のテーブルからデータを組み合わせることができます。基本的な概念でありながら、多くの開発者にとって最も混乱するトピックの1つです。このガイドではビジュアル図と実用的な例を使って、各結合タイプを明確に解説します。
サンプルデータ:2つのテーブル
このガイドでは、以下の2つのシンプルなテーブルを使用します:
-- employees table
+----+----------+---------------+
| id | name | department_id |
+----+----------+---------------+
| 1 | Alice | 1 |
| 2 | Bob | 2 |
| 3 | Charlie | 1 |
| 4 | Diana | NULL |
+----+----------+---------------+
-- departments table
+----+-------------+
| id | dept_name |
+----+-------------+
| 1 | Engineering |
| 2 | Marketing |
| 3 | Sales |
+----+-------------+INNER JOIN(内部結合)
両方のテーブルで一致する値を持つ行のみを返します。2つの集合の共通部分と考えてください。
Table A Table B
+---------+ +---------+
| | | |
| +----+-----+----+ |
| | INNER JOIN | |
| +----+-----+----+ |
| | | |
+---------+ +---------+SELECT e.name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;結果:
+----------+-------------+
| name | dept_name |
+----------+-------------+
| Alice | Engineering |
| Bob | Marketing |
| Charlie | Engineering |
+----------+-------------+
-- Diana excluded (department_id is NULL)
-- Sales excluded (no employee in Sales)両方のテーブルに存在するレコードだけが必要な場合に使用します。
LEFT JOIN(左外部結合)
左テーブルの全行と右テーブルの一致する行を返します。一致がない場合、右テーブルの列はNULLになります。
Table A Table B
+---------+ +---------+
|XXXXXXXXX| | |
|XXXX+----+-----+----+ |
|XXXX| LEFT JOIN XXXX| |
|XXXX+----+-----+----+ |
|XXXXXXXXX| | |
+---------+ +---------+
(all of A + matching B)SELECT e.name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;結果:
+----------+-------------+
| name | dept_name |
+----------+-------------+
| Alice | Engineering |
| Bob | Marketing |
| Charlie | Engineering |
| Diana | NULL |
+----------+-------------+
-- Diana included with NULL dept_name一致があるかどうかに関係なく、左テーブルの全レコードが必要な場合に使用します。
RIGHT JOIN(右外部結合)
右テーブルの全行と左テーブルの一致する行を返します。一致がない場合、左テーブルの列はNULLになります。
Table A Table B
+---------+ +---------+
| | |XXXXXXXXX|
| +----+-----+XXXX+XXXX|
| |XXXX RIGHT JOINXXXX|
| +----+-----+XXXX+XXXX|
| | |XXXXXXXXX|
+---------+ +---------+
(matching A + all of B)SELECT e.name, d.dept_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.id;結果:
+----------+-------------+
| name | dept_name |
+----------+-------------+
| Alice | Engineering |
| Charlie | Engineering |
| Bob | Marketing |
| NULL | Sales |
+----------+-------------+
-- Sales included with NULL name右テーブルの全レコードが必要な場合に使用します。
FULL OUTER JOIN(完全外部結合)
両方のテーブルの全行を返します。一致がない場合はNULLで埋めます。
Table A Table B
+---------+ +---------+
|XXXXXXXXX| |XXXXXXXXX|
|XXXX+----+-----+XXXX+XXXX|
|XXXX|FULL OUTER JOINXXXX|
|XXXX+----+-----+XXXX+XXXX|
|XXXXXXXXX| |XXXXXXXXX|
+---------+ +---------+
(everything from both)SELECT e.name, d.dept_name
FROM employees e
FULL OUTER JOIN departments d ON e.department_id = d.id;結果:
+----------+-------------+
| name | dept_name |
+----------+-------------+
| Alice | Engineering |
| Bob | Marketing |
| Charlie | Engineering |
| Diana | NULL |
| NULL | Sales |
+----------+-------------+両方のテーブルの全レコードが必要な場合に使用します。
CROSS JOIN(クロス結合)
両テーブルのデカルト積を返します。ON句は不要です。
SELECT e.name, d.dept_name
FROM employees e
CROSS JOIN departments d;
-- Returns 4 × 3 = 12 rows結果:
+----------+-------------+
| name | dept_name |
+----------+-------------+
| Alice | Engineering |
| Alice | Marketing |
| Alice | Sales |
| Bob | Engineering |
| Bob | Marketing |
| Bob | Sales |
| Charlie | Engineering |
| Charlie | Marketing |
| Charlie | Sales |
| Diana | Engineering |
| Diana | Marketing |
| Diana | Sales |
+----------+-------------+組み合わせの生成、テストデータ、カレンダーグリッドに使用します。大きなテーブルでは避けてください。
自己結合(Self JOIN)
テーブルを自分自身と結合します。階層データや同じテーブル内の行比較に便利です。
-- employees_v2 with manager_id column
+----+---------+------------+
| id | name | manager_id |
+----+---------+------------+
| 1 | Alice | NULL |
| 2 | Bob | 1 |
| 3 | Charlie | 1 |
| 4 | Diana | 2 |
+----+---------+------------+
SELECT
e.name AS employee,
m.name AS manager
FROM employees_v2 e
LEFT JOIN employees_v2 m ON e.manager_id = m.id;結果:
+----------+---------+
| employee | manager |
+----------+---------+
| Alice | NULL |
| Bob | Alice |
| Charlie | Alice |
| Diana | Bob |
+----------+---------+従業員とマネージャーの関係、階層カテゴリ、行の比較に使用します。
どの結合を使うべき?クイックリファレンス
| 結合タイプ | 返す内容 | 最適な使用場面 |
|---|---|---|
| INNER JOIN | A ∩ B | 一致データのみ |
| LEFT JOIN | A + (A ∩ B) | 左テーブル全部 + 一致 |
| RIGHT JOIN | (A ∩ B) + B | 右テーブル全部 + 一致 |
| FULL OUTER JOIN | A ∪ B | 両テーブルの全データ |
| CROSS JOIN | A × B | 全組み合わせ |
| Self JOIN | 結合タイプ依存 | 階層/自己参照データ |
パフォーマンスのヒント
- JOIN列には必ずインデックスを作成してください。
- 本番クエリではSELECT *を避け、必要な列のみ選択。
- 可能であれば小さいテーブルをFROM句の先頭に配置。
- EXPLAIN ANALYZEでクエリ実行計画を確認。
- 相関サブクエリではINよりEXISTSを優先。
- 式や関数での結合を避ける(インデックスが使えなくなる)。
-- Create index on JOIN column
CREATE INDEX idx_emp_dept ON employees(department_id);
-- Use EXPLAIN to check query plan
EXPLAIN ANALYZE
SELECT e.name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;よくある間違い
| 間違い | 問題 | 修正方法 |
|---|---|---|
| Using SELECT * | Returns unnecessary columns, wastes bandwidth | Select only needed columns |
| Missing index on JOIN column | Full table scan, extremely slow | CREATE INDEX |
| NULL comparison trap | NULL = NULL evaluates to FALSE | Use IS NULL or COALESCE |
| Accidental Cartesian product | Missing ON condition, row explosion | Always verify ON clause |
| Filtering OUTER JOIN in WHERE | WHERE filter turns LEFT JOIN into INNER JOIN | Put conditions in ON clause |
| Unnecessary DISTINCT | Hides incorrect JOIN logic | Fix JOIN condition, don't add DISTINCT |
| Joining on functions | Index unusable, forces full scan | Store normalized data or use computed columns |
-- ❌ Wrong: WHERE turns LEFT JOIN into INNER JOIN
SELECT e.name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id
WHERE d.dept_name = 'Engineering'; -- filters out NULLs!
-- ✅ Correct: Put condition in ON clause
SELECT e.name, d.dept_name
FROM employees e
LEFT JOIN departments d
ON e.department_id = d.id
AND d.dept_name = 'Engineering'; -- preserves NULLsよくある質問
INNER JOINとOUTER JOINの違いは?
INNER JOINは両テーブルの一致する行のみ返します。OUTER JOINは一致しない行も含め、NULLで埋めます。
3つ以上のテーブルを結合できますか?
はい。複数のJOINをチェーンできます。実質的な制限はありません。
INNER JOINでON句を忘れるとどうなりますか?
ほとんどのSQL方言では構文エラーになります。一部ではCROSS JOINになる可能性があります。
LEFT JOINとLEFT OUTER JOINは同じですか?
はい。OUTERキーワードはオプションです。全く同じです。
どの結合が最も速いですか?
INNER JOINは一般的に最速です。しかし実際のパフォーマンスはインデックス、テーブルサイズ、オプティマイザに依存します。
NATURAL JOINとは何ですか?
同名の列で自動的に結合します。列の追加で動作が変わる可能性があるため、本番では非推奨です。