6.

SQL JOIN 完全ガイド (INNER/LEFT/RIGHT/FULL OUTER)

編集
この記事の要点
  • SQL JOIN は複数テーブルを結合する基本構文。主要 5 種は INNER / LEFT / RIGHT / FULL OUTER / CROSS
  • OUTER JOIN では ON 条件と WHERE 条件で結果が変わる重要ポイント
  • USING (col) は両表で同名カラムを結合する簡略記法。重複カラムは 1 列にまとまる
  • EXISTS / NOT EXISTS は SEMI / ANTI JOIN を表現。性能面で INNER/LEFT より良いこともある
  • Join Algorithm は Nested Loop / Hash Join / Sort-Merge Join。インデックス・統計次第で最適解が変わる

JOIN の基本

SQL の JOIN は、複数テーブルを共通のキーで結合し、1 つの結果セットを得る構文。FROM A JOIN B ON A.x = B.x の形式が標準。

-- サンプル
CREATE TABLE employees (id INT, name VARCHAR(50), dept_id INT);
CREATE TABLE departments (id INT, name VARCHAR(50));

INSERT INTO employees VALUES
  (1,'Alice',10), (2,'Bob',20), (3,'Carol',NULL);
INSERT INTO departments VALUES
  (10,'Sales'), (20,'Engineering'), (30,'Marketing');

5 種類の JOIN

JOIN 種類左にのみ存在両方に存在右にのみ存在
INNER JOIN
LEFT [OUTER] JOIN○(右側 NULL)
RIGHT [OUTER] JOIN○(左側 NULL)
FULL OUTER JOIN
CROSS JOIN全組合せ(デカルト積)

INNER JOIN

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

-- 結果
-- Alice  | Sales
-- Bob    | Engineering
-- (Carol は dept_id=NULL なので除外、Marketing は employee なしで除外)

LEFT OUTER JOIN

SELECT e.name, d.name AS dept
FROM   employees e
LEFT   JOIN departments d ON e.dept_id = d.id;

-- 結果
-- Alice  | Sales
-- Bob    | Engineering
-- Carol  | NULL          ← 左に居る人は必ず出る

RIGHT OUTER JOIN

SELECT e.name, d.name AS dept
FROM   employees e
RIGHT  JOIN departments d ON e.dept_id = d.id;

-- 結果
-- Alice  | Sales
-- Bob    | Engineering
-- NULL   | Marketing     ← 右に居る部署は必ず出る

FULL OUTER JOIN

左右どちらかに存在する全ての行を返す。MySQL は標準では非対応UNION で代用。

-- PostgreSQL / Oracle / SQL Server
SELECT e.name, d.name AS dept
FROM   employees e
FULL   OUTER JOIN departments d ON e.dept_id = d.id;

-- MySQL は LEFT UNION RIGHT で代替
SELECT e.name, d.name FROM employees e LEFT JOIN departments d ON e.dept_id = d.id
UNION
SELECT e.name, d.name FROM employees e RIGHT JOIN departments d ON e.dept_id = d.id;

CROSS JOIN (デカルト積)

SELECT e.name, d.name FROM employees e CROSS JOIN departments d;
-- 3 employees × 3 departments = 9 行

-- 古典的記法(カンマ)でも同じ
SELECT e.name, d.name FROM employees e, departments d;

SELF JOIN

同じテーブルを自分自身と結合。階層 / 上司部下関係などで活躍。

-- 社員と上司
SELECT e.name AS employee, m.name AS manager
FROM   employees e
LEFT   JOIN employees m ON e.manager_id = m.id;

USING 句

結合キーが両表で同じカラム名のとき、USING (col) で簡潔に書ける。結合カラムは結果で 1 列にマージされる。

-- 両表に dept_id がある場合
SELECT * FROM employees JOIN departments USING (dept_id);
-- ON e.dept_id = d.dept_id と同じだが
-- 結果に dept_id が 1 列だけ出る(ON だと 2 列)

NATURAL JOIN

両表で同名カラム全てを自動結合。意図しない結合が起きるため避けるのがベストプラクティス。

SELECT * FROM employees NATURAL JOIN departments;
-- 両表に created_at があると、それも結合キーに使われて事故る

複数 JOIN

SELECT o.id, c.name AS customer, p.name AS product, oi.quantity
FROM   orders o
JOIN   customers c ON o.customer_id = c.id
JOIN   order_items oi ON oi.order_id = o.id
JOIN   products p ON oi.product_id = p.id
WHERE  o.created_at >= '2026-06-01';

SEMI JOIN / ANTI JOIN

「片方に存在するか / しないか」を判定して左表の行だけ返す JOIN。EXISTS / NOT EXISTS で表現。

-- SEMI JOIN: 注文がある顧客
SELECT * FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);

-- ANTI JOIN: 注文が一度もない顧客
SELECT * FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);

-- LEFT JOIN + IS NULL でも同じ表現可(ANTI JOIN)
SELECT c.*
FROM   customers c
LEFT   JOIN orders o ON o.customer_id = c.id
WHERE  o.id IS NULL;

ON と WHERE の違い (OUTER JOIN)

OUTER JOIN では、フィルタを ON に書くか WHERE に書くかで意味が変わります。INNER JOIN では差はありません。

-- 例: アクティブな部署のみを左結合したいが
-- 部署が無い社員は残したい

-- ✅ ON に書く: Carol(部署無し) は残る
SELECT e.name, d.name
FROM   employees e
LEFT   JOIN departments d
       ON e.dept_id = d.id AND d.active = 1;

-- ❌ WHERE に書く: NULL がフィルタで弾かれ Carol が消える
SELECT e.name, d.name
FROM   employees e
LEFT   JOIN departments d ON e.dept_id = d.id
WHERE  d.active = 1;       -- d.active が NULL の Carol は除外される

Join Algorithm (Optimizer の選択)

アルゴリズム得意な場面計算量
Nested Loop Join外側が小さく、内側にインデックスありO(N×log M)
Hash Join大量データ、結合キーに索引なしO(N+M)
Sort-Merge Join両表が結合キーで既にソート済O(N log N + M log M)
Index Nested Loop外側小 + 内側 PK 等価結合★ 典型的 OLTP
-- 実行計画の確認
EXPLAIN ANALYZE SELECT ...           -- PostgreSQL
EXPLAIN SELECT ...                   -- MySQL
EXPLAIN PLAN FOR SELECT ...; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);  -- Oracle
SET STATISTICS PROFILE ON;           -- SQL Server

性能のコツ

  • 結合キーにインデックスを張る(外部キー側は特に)
  • 大きい表は後ろに書くと NLJ で有利(多くの DB が自動最適化するが目安)
  • WHERE で先に絞り込んでから JOIN させると I/O が減る(オプティマイザの仕事だが書き方も影響)
  • 不要な列を SELECT しない(カバーリングインデックス効果)
  • 統計情報を最新に保つ(ANALYZE / UPDATE STATISTICS

FAQ

Q: RIGHT JOIN はいつ使う?
A: ほぼ使いません。FROM B RIGHT JOIN AFROM A LEFT JOIN B と等価で、可読性のため LEFT に統一するのが慣習。

Q: INNER JOIN ON 1=1 は何?
A: 結合条件「常に真」= CROSS JOIN。デカルト積を作りたいときに書きますが、意図を明確にするため CROSS JOIN 構文を使う方が良いです。

Q: NULL は JOIN でどう扱われる?
A: NULL = NULLになるため、結合キーが NULL の行は INNER JOIN で常に除外されます。意図して残したいなら IS NOT DISTINCT FROM (PostgreSQL) や NVL で代替値に置換します。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. SELECT文
  2. INSERT
  3. UPDATE文
  4. DELETE文
  5. WHERE句
  6. JOIN句
  7. 集合演算子
  8. インラインビュー
  9. 副問い合わせ (サブクエリ)

最近更新/作成されたページ