TL

JOIN(テーブルの結合)

正規化でバラバラに分けたテーブルを、共通のキーでつなぎ合わせて1つの結果として取り出す操作。INNER / OUTER の違いが肝。

中級SQLRDBクエリ最終更新: 2026-06-04
TL;DR要点だけ先に
  • 1.JOIN は、別々のテーブルを“結合キー”でつなぎ、1つの表として取り出す操作。
  • 2.INNER は両方に一致した行だけ、LEFT/RIGHT/FULL OUTER は片側(or 両側)に無くても残す。
  • 3.ループ内で1件ずつ引くと N+1 問題。JOIN か IN でまとめて取るのが定石。

なぜ JOIN が要るのか

RDB では、同じ情報を何度も書かない(正規化する)のが基本です。例えば注文ごとにユーザー名を毎回コピーすると、改名時に全行を直す羽目になり、矛盾も生まれます。そこで——

  • users テーブル … ユーザーの情報(id, name
  • orders テーブル … 注文の情報(id, user_id, amount

と分け、orders.user_id で「どのユーザーか」を参照(外部キー)だけ持たせます。分けた代償として、「注文と、その注文をしたユーザー名」を一緒に見たいときにつなぎ直す必要が出てきます。それが JOIN です。

-- 注文に、注文者の名前をくっつけて取り出す
SELECT orders.id, users.name, orders.amount
FROM orders
JOIN users ON orders.user_id = users.id;

ON orders.user_id = users.id結合条件で、ここで使う列を結合キーと呼びます。多くは「片方の主キー(users.id)」と「もう片方の外部キー(orders.user_id)」を突き合わせます。

図的な対応イメージ

JOIN は、2つの表を結合キーで横に並べてくっつけるイメージです。

users                 orders                       結合後(user_id = id で対応)
+----+-------+        +----+---------+--------+    +----------+-------+--------+
| id | name  |        | id | user_id | amount |    | order_id | name  | amount |
+----+-------+        +----+---------+--------+    +----------+-------+--------+
|  1 | Alice |   ←──  |  1 |    1    |  500   | →  |    1     | Alice |  500   |
|  2 | Bob   |   ←──  |  2 |    1    |  300   | →  |    2     | Alice |  300   |
|  3 | Carol |   ←──  |  3 |    2    |  800   | →  |    3     | Bob   |  800   |
+----+-------+        +----+---------+--------+    +----------+-------+--------+
                       (Carol は注文なし → INNER では消える / LEFT では残る)

ポイントは、1対多だと「多」の側の行数だけ結果が増えること。Alice は注文2件なので、結合後に Alice が2行に展開されます。

4種類の JOIN

「どちらのテーブルに行が無くても残すか」で種類が分かれます。これが JOIN 最大の論点です。

種類残す行ひとことで
INNER JOIN両方に一致する行だけマッチしたペアのみ。注文の無いユーザーは消える
LEFT (OUTER) JOIN左の全行+右の一致左を主役に。右が無ければ NULL で埋める
RIGHT (OUTER) JOIN右の全行+左の一致LEFT の左右逆。実務では LEFT に書き換えがち
FULL OUTER JOIN両方の全行どちらかに有れば残す。無い側は NULL

LEFT JOIN を使うと、「注文が1件も無いユーザー」も結果に残り、注文側の列が NULL になります。

-- 全ユーザーを、注文が無い人も含めて一覧(注文数が 0 の人も出る)
SELECT users.name, COUNT(orders.id) AS order_count
FROM users
LEFT JOIN orders ON users.id = orders.user_id
GROUP BY users.name;
-- Carol → order_count = 0 で出る(INNER JOIN だと Carol は消える)
“OUTER” は飾り、省略OK

LEFT OUTER JOINLEFT JOIN は完全に同じ。OUTER は付けても付けなくても挙動は変わりません。逆に何も付けない JOININNER JOIN と同義です。

つまずきポイント

LEFT JOIN の右側を WHERE で絞ると INNER に化ける

LEFT JOIN で残したはずの「右が NULL の行」も、WHERE orders.amount > 100 のように右テーブルの列を WHERE で条件にすると消えます(NULL は比較で真にならないため)。OUTER 側の条件は WHERE ではなく ON 句に書くのが鉄則。「LEFT のつもりが結果が減る」典型ワナです。

結合キーの指定漏れ=クロス結合の事故

ON を書き忘れたり、条件が緩すぎると、全行 × 全行の総当たり(クロス結合)になります。1万行 × 1万行 = 1億行が一瞬で生まれ、DB が固まります(意図せず CROSS JOIN を書いた状態)。JOIN したら結合キーが正しく1対1で効いているかを必ず確認しましょう。

N+1 問題

JOIN を避けて「アプリ側のループで1件ずつ引く」と、N+1 問題に陥ります。

1回目: ユーザー一覧を取得          → SELECT * FROM users           (1回)
2回目以降: ユーザーごとに注文を取得 → SELECT * FROM orders WHERE... (N回)
合計 = 1 + N 回のクエリ

ユーザーが1,000人なら1,001回もDBに往復します。1回あたりの通信が速くても、回数が積み重なると致命的に遅くなります。ORM(O/Rマッパー)が裏で自動発行しがちで、気づきにくいのが厄介です。

観点N+1(1件ずつループ)JOIN / IN でまとめ取り
クエリ回数1 + N 回1 回(または数回)
DBとの往復件数ぶん発生し遅い1往復で完結
典型の原因ORM の遅延ロードJOIN / IN / eager loading
対処結合してまとめて取得する

対処は、JOIN で一度に取るか、WHERE user_id IN (...) でまとめて引くこと。ORM なら「eager loading(先読み)」の機能を使います。

N が多い JOIN は INDEX とセットで

結合キー(特に外部キー側 orders.user_id)にインデックスが無いと、JOIN のたびに全行スキャンになり遅くなります。「結合キーには索引を張る」がパフォーマンスの基本です。

まとめ

  • JOIN は、正規化で分けたテーブルを結合キーでつなぎ直す操作。
  • INNER=一致のみ、LEFT/RIGHT/FULL OUTER=片側(両側)に無くても残す。違いは「残す行」で覚える。
  • OUTER 側の条件は ON に書く(WHERE に書くと INNER 化)。結合キー漏れはクロス結合の事故
  • ループで1件ずつ引く N+1 問題は、JOIN か IN でまとめて取り、結合キーにインデックスを張って解消する。

データベース Article

JOIN(テーブルの結合)を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

SQL

比較で見る軸

難易度: intermediate / カテゴリ: データベース / タグ数: 3

導入後に効く点

INNER は両方に一致した行だけ、LEFT/RIGHT/FULL OUTER は片側(or 両側)に無くても残す。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
intermediate
カテゴリ
データベース
タグ数
3

判断チェックリスト

  • 自社の用途が「SQL / RDB」に近いか確認する。
  • 強みである「JOIN は、別々のテーブルを“結合キー”でつなぎ、1つの表として取り出す操作。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

SQLRDBクエリSQLRDBクエリ
参考: 公式情報