動くコード図鑑技術記事現場の渡り方キャリア論すべての記事About
技術記事

SQL Server の JOIN 完全ガイド — INNER / LEFT / RIGHT / FULL / CROSS を業務SEが件数で腹落ちさせる

バイブス父さん
現役の業務SE
2026年7月16日10 min read
SQL Server の JOIN 完全ガイド — INNER / LEFT / RIGHT / FULL / CROSS を業務SEが件数で腹落ちさせる

みなさんこんにちは!ヒロポンです!

LEFT JOIN で書いたのに、なぜか件数が減る。「あれ、全部残るはずやのに??」って夜中にログ追ったこと、あります。

原因はシンプルで、結合の種類を分かってなかっただけ。

SQL Server の結合は INNER / LEFT / RIGHT / FULL / CROSS の5種類。どれを選ぶかで結果の件数が変わるんですよね。ここが腹落ちしてないと集計がズレて、原因究明に時間を持っていかれます。

今回はSQL Server JOIN 種類の5つを、同じデータで件数がどう変わるか見せながら、1つずつ潰していきます。

そもそも JOIN とは — 2つのテーブルを横に突き合わせる

JOIN は、2つのテーブルをキーで突き合わせて横につなげる操作です。

たとえば「注文テーブル」と「顧客テーブル」。注文には顧客ID しか入ってないのに、画面には顧客名を出したい。そういう時、顧客ID をキーにして2つを結合します。

問題は「一致しなかった行をどうするか」。ここの扱いが、5種類の違いそのものなんですよね。

検証用データ(注文と顧客)

話を具体的にするために、このデータで統一します。

CREATE TABLE dbo.Customers (Id INT, Name NVARCHAR(20));
CREATE TABLE dbo.Orders    (OrderId INT, CustomerId INT);

INSERT INTO dbo.Customers VALUES (1, N'田中'), (2, N'佐藤'), (3, N'鈴木');
INSERT INTO dbo.Orders    VALUES (101, 1), (102, 1), (103, 2), (104, NULL);

わざと「はみ出す」データを2つ仕込んでます。

  • 顧客3(鈴木)は注文が無い(顧客側にだけ存在)
  • 注文104 は CustomerId が NULL(注文側にだけ存在・顧客不明)

この「はみ出し」が、結合の種類でどう扱われるか。ここを追っていきます。

INNER JOIN — 両方に一致する行だけ

一番基本のやつ。両方のテーブルにキーが一致する行だけを返します。

SELECT o.OrderId, c.Name
FROM dbo.Orders o
INNER JOIN dbo.Customers c ON o.CustomerId = c.Id;

結果は 3件(101→田中 / 102→田中 / 103→佐藤)。

注文104(CustomerId が NULL)と顧客3(鈴木・注文なし)は、相手がいないので消えます。「両方にある行だけ」。ここだけ覚えとけばいい。

LEFT JOIN と RIGHT JOIN — 片方を全部残す

LEFT JOIN は、左のテーブル(FROM 側)を全部残す。右に相手がいなければ、右の列は NULL で埋まります。

SELECT o.OrderId, c.Name
FROM dbo.Orders o
LEFT JOIN dbo.Customers c ON o.CustomerId = c.Id;

結果は 4件。INNER の3件に加えて、注文104 が Name = NULL で残ります(注文は全部残るから)。こんな感じで、左は1行も落ちません。

RIGHT JOIN はその逆。右のテーブル(Customers)を全部残す

SELECT o.OrderId, c.Name
FROM dbo.Orders o
RIGHT JOIN dbo.Customers c ON o.CustomerId = c.Id;

結果は 4件。INNER の3件に加えて、顧客3(鈴木)が OrderId = NULL で残ります(顧客は全部残るから)。

実務では LEFT JOIN が圧倒的に多い。RIGHT はテーブルの順番を入れ替えれば LEFT で書けるので、こんな感じで「基本は LEFT に寄せる」と読みやすくなります。

FULL JOIN と CROSS JOIN — 全行、そして全組み合わせ

FULL JOIN は、両方のはみ出しを全部残す。LEFT と RIGHT を合体させたイメージですね。こんな感じで、どっちも落とさない。

SELECT o.OrderId, c.Name
FROM dbo.Orders o
FULL JOIN dbo.Customers c ON o.CustomerId = c.Id;

結果は 5件。一致した3件+注文104(Name NULL)+顧客3(OrderId NULL)。「どっちのテーブルからも1行も落としたくない」時に使います。

CROSS JOIN は毛色が違う。ON 句なしで全組み合わせ(直積)を作ります。

SELECT o.OrderId, c.Name
FROM dbo.Orders o
CROSS JOIN dbo.Customers c;

結果は 12件(注文4 × 顧客3)。全部の掛け合わせなので件数が一気に膨らむ。カレンダー表を作る時なんかに使いますが、業務でうっかり書くと件数爆発の事故になるので注意です。

5種類の結果を1枚で見る

同じデータで、件数がこう変わります。ここが今回の核心です!!

SQL Server の JOIN 5種類(INNER/LEFT/RIGHT/FULL/CROSS)を、残す行・サンプル件数・NULL埋め・書き方で比較した表

実行結果(5種類 + ハマり①の件数を SQL Server 2019 互換エンジンで実測):

SQL Server JOIN 5種類の結果件数の実測(INNER 3 / LEFT 4 / RIGHT 4 / FULL 5 / CROSS 12)

「件数が想定と違う」時は、まずこの表で「自分はどれを使ってるか」を確認する。これが一番早いです。

ハマりポイント: 知らないと件数がズレるやつ

結合まわりで業務SEがよく踏むのは、この2つ。

① LEFT JOIN したのに WHERE で右列を条件にすると、実質 INNER になる

正直、俺が夜中に溶かしたのがこれです。LEFT JOIN で全部残したつもりが、WHERE に右テーブルの列を書いた瞬間、NULL 行が消えて実質 INNER JOINになってた。

-- ❌ LEFT のつもりが、WHERE で右列を条件にすると NULL 行が落ちる
SELECT o.OrderId, c.Name
FROM dbo.Orders o
LEFT JOIN dbo.Customers c ON o.CustomerId = c.Id
WHERE c.Name = N'田中';   -- c.Name が NULL の注文104 は、この条件で除外される

WHERE c.Name = '田中' は、Name が NULL の行を問答無用で落とします(NULL は条件に一致しないので)。せっかく LEFT で残した NULL 行が、ここで消える。あなたもこれ、やったことあるでしょ?

直し方。右テーブルへの絞り込みは WHERE じゃなく ON 句に書く

-- ✅ 右列の条件は ON 句へ。LEFT の NULL 行は残る
SELECT o.OrderId, c.Name
FROM dbo.Orders o
LEFT JOIN dbo.Customers c ON o.CustomerId = c.Id AND c.Name = N'田中';

教訓はLEFT JOIN で右テーブルを絞るなら、条件は ON に書くWHERE に書くと、LEFT が INNER に化けます。

② 結合キーに NULL があると、一致しない

これもハマりやすいやつ。SQL の NULL は「値が不明」なので、NULL = NULL は成立しません(真にならない)。

今回の注文104 は CustomerId が NULL。だから顧客テーブルに「Id が NULL の顧客」を入れても、o.CustomerId = c.Id では結合されない。NULL 同士は等しくない。ん?NULL なのに一致せえへんの??ってなるやつです。

「結合キーに NULL が混じるデータ」を扱う時は、ISNULL(CustomerId, -1) みたいに NULL を実在しない値へ置き換えてから結合する。もしくは、そもそも結合キーに NULL を入れない設計にする。どっちかが安全です。

俺の現場での考え方

流通系の基幹システムの保守をやってた頃、結合の種類を意識せずコピペで JOIN を書いてる箇所が山ほどあって、件数が合わない調査でよく時間を取られてました。

そこから落とし込んだのはシンプルで、こう決めてます。

  • 基本は INNER。「相手がいない行も残したい」時だけ LEFT に切り替える
  • LEFT を書いたら、右テーブルの条件は必ず ONWHERE に書かない)
  • 結合キーに NULL がありうるなら、結合前に潰す

「件数が想定と違う」と思ったら、まず結合の種類と WHERE の位置を見る。これだけで調査時間がガクッと減ります。

まとめ

要点はこれだけ。

  • INNER=両方に一致する行だけ / LEFT=左を全部 / RIGHT=右を全部 / FULL=両方の全行 / CROSS=全組み合わせ
  • 使う種類で件数が変わる。「想定と違う」ならまず種類を疑う
  • LEFT JOIN の右列条件は WHERE ではなく ON に書く(でないと実質 INNER)
  • 結合キーの NULL は一致しない。事前に潰すか設計で避ける

SQL Server の結合や実行計画をもっと深く押さえたいなら、RDB の仕組み系の解説書を1冊持っておく。この手の「なんで件数が違う?」を調べる時間が、まるっと浮きます。

このガイド、結合クエリを書く時の手元メモにでも使ってください。

よくある質問

Q1. LEFT JOIN と LEFT OUTER JOIN は違いますか?

同じです。OUTER は省略できるキーワードで、LEFT JOINLEFT OUTER JOIN はまったく同じ動きをします。RIGHT / FULL も同様に OUTER を省略できます。INNER も省略して単に JOIN と書けますが、意図を明示するために書いておくのが無難です。

Q2. 結合したら件数が増えました。なぜですか?

結合キーが「1対多」になってるのが原因のことが多いです。今回の例でも、顧客1(田中)に注文が2件あるので、田中は結果に2行出ます。1対多の結合では、多い側の件数に膨らむ。想定より増えたら、結合キーの重複を疑ってください。重複排除は SQL Server DISTINCT の3つの罠 も参考になります。

Q3. INNER JOIN と WHERE での結合、どちらを使うべきですか?

WHERE a.Id = b.Id の書き方(暗黙結合)でも INNER JOIN と同じ結果になりますが、INNER JOIN ... ON の明示結合を推奨します。結合条件と絞り込み条件が分離されて読みやすく、LEFT/RIGHT への変更もしやすい。古いコードで暗黙結合を見かけたら、明示結合に直しておくと保守が楽になります。

次に読むべき記事

以上!

同じ「LEFT のはずが件数減った」で溶かした人いたら、どんどんシェア待ってるぜ!!


執筆者

バイブス父さん — 業務 SE 7 年 (SIer 正社員 2 / フリーランス 5)。 現職は SEO 直轄部の AI アドバイザー兼 PL、 副業で中小 SIer の CTO。 SIer の正社員からフリーランスに転じ、 複数のエージェント経由で案件を回してきた経験ベースで「業務 SE 視点」 の技術 + キャリア記事を書いています。

🐦 X: @hiro_progra0524 (日々の現場メモ更新中)
📝 About Me で経歴詳細を見る

この記事のコードと手順は ぜんぶ動作検証済み。 安心して現場で試してくれ。
バイブス父さん

現役の業務SE。C# / SQL Server 保守の現場から、コードも人もキャリアも全部書く。 実体験ベース。

運営者について