DevToolBox無料
ブログ

SQL Joinを図解で完全理解:実例付きビジュアルガイド

12分by DevToolBox

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 JOINA ∩ B一致データのみ
LEFT JOINA + (A ∩ B)左テーブル全部 + 一致
RIGHT JOIN(A ∩ B) + B右テーブル全部 + 一致
FULL OUTER JOINA ∪ B両テーブルの全データ
CROSS JOINA × 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 bandwidthSelect only needed columns
Missing index on JOIN columnFull table scan, extremely slowCREATE INDEX
NULL comparison trapNULL = NULL evaluates to FALSEUse IS NULL or COALESCE
Accidental Cartesian productMissing ON condition, row explosionAlways verify ON clause
Filtering OUTER JOIN in WHEREWHERE filter turns LEFT JOIN into INNER JOINPut conditions in ON clause
Unnecessary DISTINCTHides incorrect JOIN logicFix JOIN condition, don't add DISTINCT
Joining on functionsIndex unusable, forces full scanStore 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とは何ですか?

同名の列で自動的に結合します。列の追加で動作が変わる可能性があるため、本番では非推奨です。

𝕏 Twitterin LinkedIn
この記事は役に立ちましたか?

最新情報を受け取る

毎週の開発ヒントと新ツール情報。

スパムなし。いつでも解除可能。

Try These Related Tools

SQLSQL FormatterPSSQL to Prisma Schema{ }JSON Formatter

Related Articles

JSON vs YAML vs TOML:どの設定フォーマットを使うべき?

JSON、YAML、TOML の設定フォーマットを比較。

Prisma Schema とリレーション ガイド

Prismaスキーマ設計をマスター:モデル、リレーション(1:1、1:N、M:N)、enum、インデックス、マイグレーション。