【SQL】JOINの使い方|INNER JOIN・LEFT JOINの違いを実行結果で確認
SQLのJOINは、共通する列を条件にして、複数のテーブルを組み合わせる書き方です。一致した行だけが必要ならINNER JOIN、左のテーブルをすべて残したいならLEFT JOINを使います。
たとえば「顧客名と注文した商品を一緒に表示したい」ときに使います。この記事では顧客と注文の小さなデータで、書き方と結果の違いを見ていきましょう。
実行環境はSQL Server 2019 LocalDBです。練習用データの作成部分はSQL Server用なので、MySQLなどで試す場合は一時テーブルや文字列の書き方を読み替えてください。
INNER JOINとLEFT JOINの違い
| 書き方 | 結果に残る行 | 使う場面 |
|---|---|---|
| INNER JOIN | 結合条件に一致した組み合わせだけ | 注文のある顧客と商品を表示する |
| LEFT JOIN | 左側の全行と、一致した右側の行 | 注文のない顧客も表示する |
INNER JOINは「内部結合」、LEFT JOINは「左外部結合」と呼ばれます。JOINだけならINNER JOIN、LEFT OUTER JOINはLEFT JOINと同じ意味です。
LEFT JOINでいう「左」は、FROMの直後に書いたテーブルです。右側に一致する行がないと、右側の列はNULLになります。NULLは、空文字や0とは別の値です。
実習用の顧客・注文データを用意する
SSMSで検証用の接続を開き、「新しいクエリ」に次のSQLを貼り付けて実行します。SSMSの準備がまだの場合は、SQL Serverの導入と接続方法を確認してください。
SET NOCOUNT ON;
DROP TABLE IF EXISTS #JoinOrders;
DROP TABLE IF EXISTS #JoinCustomers;
CREATE TABLE #JoinCustomers (
CustomerID int PRIMARY KEY,
CustomerName nvarchar(20) NOT NULL
);
CREATE TABLE #JoinOrders (
OrderID int PRIMARY KEY,
CustomerID int NOT NULL,
ProductName nvarchar(20) NOT NULL,
OrderStatus nvarchar(10) NOT NULL
);
INSERT INTO #JoinCustomers VALUES
(1, N'サンプルA店'),
(2, N'サンプルB店'),
(3, N'サンプルC店');
INSERT INTO #JoinOrders VALUES
(101, 1, N'ノート', N'確定'),
(102, 1, N'ペン', N'未確定'),
(103, 2, N'バッグ', N'確定');先頭に#が付くテーブルは、この接続で使うローカル一時テーブルです。既存の顧客・注文テーブルは使いません。以降のSELECTは、作成したときと同じクエリウィンドウで実行してください。接続を閉じると一時テーブルも削除されます。
顧客は次の3件です。サンプルC店には注文がありません。
| 顧客ID | 顧客名 |
|---|---|
| 1 | サンプルA店 |
| 2 | サンプルB店 |
| 3 | サンプルC店 |
注文は次の3件です。CustomerIDで、どの顧客の注文なのかを表します。
| 注文ID | 顧客ID | 商品名 | 注文状況 |
|---|---|---|---|
| 101 | 1 | ノート | 確定 |
| 102 | 1 | ペン | 未確定 |
| 103 | 2 | バッグ | 確定 |
INNER JOINで一致した行だけ取得する
SELECT c.CustomerID AS 顧客ID, c.CustomerName AS 顧客名,
o.OrderID AS 注文ID, o.ProductName AS 商品名
FROM #JoinCustomers AS c
INNER JOIN #JoinOrders AS o
ON c.CustomerID = o.CustomerID
ORDER BY c.CustomerID, o.OrderID;ONの条件で、顧客側と注文側のCustomerIDを対応させています。cとoはテーブルの別名で、c.CustomerIDのように書くと「どちらのテーブルの列か」が分かります。
結果は3行です。A店は注文が2件あるので2行、B店は1行になります。注文のないC店は含まれません。
SELECTやORDER BYの基本は、SELECT文の使い方でも説明しています。この記事では結果を見比べやすいように、顧客ID・注文IDの順で並べています。
LEFT JOINで注文のない顧客も残す
INNER JOINをLEFT JOINに変えて実行してみましょう。
SELECT c.CustomerID AS 顧客ID, c.CustomerName AS 顧客名,
o.OrderID AS 注文ID, o.ProductName AS 商品名
FROM #JoinCustomers AS c
LEFT JOIN #JoinOrders AS o
ON c.CustomerID = o.CustomerID
ORDER BY c.CustomerID, o.OrderID;結果は4行です。C店も残り、注文IDと商品名はNULLになります。A店には注文が2件あるので、LEFT JOINでも1行にはまとまりません。
JOINは「左の1行につき結果も1行」とは限りません。一致する注文が2件あれば2行になります。行が増えたときは、まず結合する列と、相手側の同じ値が何件あるかを確認しましょう。
LEFT JOINの条件はONとWHEREで結果が変わる
「確定した注文だけを表示したい。ただし注文のない顧客も残したい」という場合は、注文の条件をONに足します。
ONに条件を書く:顧客をすべて残す
SELECT c.CustomerID AS 顧客ID, c.CustomerName AS 顧客名,
o.OrderID AS 注文ID, o.ProductName AS 商品名
FROM #JoinCustomers AS c
LEFT JOIN #JoinOrders AS o
ON c.CustomerID = o.CustomerID
AND o.OrderStatus = N'確定'
ORDER BY c.CustomerID, o.OrderID;結合相手を「顧客IDが同じ、かつ注文が確定している行」に限定します。結果はA店・B店・C店の3行で、C店の注文欄はNULLです。
WHEREに条件を書く:条件を満たさない行を除く
SELECT c.CustomerID AS 顧客ID, c.CustomerName AS 顧客名,
o.OrderID AS 注文ID, o.ProductName AS 商品名
FROM #JoinCustomers AS c
LEFT JOIN #JoinOrders AS o
ON c.CustomerID = o.CustomerID
WHERE o.OrderStatus = N'確定'
ORDER BY c.CustomerID, o.OrderID;こちらは結合した結果をWHEREで絞り込みます。C店の注文状況はNULLなので「確定」という条件を満たさず、結果から外れます。結果はA店とB店の2行です。
| 条件を書く場所 | この例の結果 | C店 |
|---|---|---|
| ON | 3行 | 残る |
| WHERE | 2行 | 残らない |
これは右側の注文状況を条件にした場合の違いです。WHEREを使うと必ずLEFT JOINがINNER JOINになる、という意味ではありません。残したい行を決めてから、条件を書く場所を選びましょう。
注文のない顧客だけを探す
LEFT JOINで結合したあと、右側の注文IDがNULLになった行を取り出します。
SELECT c.CustomerID AS 顧客ID, c.CustomerName AS 顧客名
FROM #JoinCustomers AS c
LEFT JOIN #JoinOrders AS o
ON c.CustomerID = o.CustomerID
WHERE o.OrderID IS NULL
ORDER BY c.CustomerID;この例では、顧客IDが3のサンプルC店だけが表示されます。注文IDは主キーでNULLを許可していないため、一致する注文がなかったかどうかを判断できます。
NULLの判定にはIS NULLを使います。「= NULL」では、この結果を取り出せません。また、NULLを許可する商品名などで判定すると、注文が存在する行まで拾うことがあるため注意しましょう。
JOINでつまずいたときの確認ポイント
「列名があいまいです」と出る
両方のテーブルにあるCustomerIDなどは、c.CustomerID、o.CustomerIDのように別名を付けて指定します。表示したい列が、どちらのテーブルにあるかも確認してください。
予想より行数が増える
同じ顧客に注文が複数あれば、その組み合わせの数だけ行が返ります。結合条件の列が違っていないか、複数列で対応させる必要がないかを確認します。理由を確かめずにDISTINCTで消すと、必要な注文までまとめてしまうことがあります。
LEFT JOINなのに顧客が消える
右側の列をWHEREで絞り込んでいないか見直します。全顧客を残して注文の結合条件だけを変えたい場合は、この記事のONとWHEREの例を比べてみてください。
一時テーブルが見つからない
別のクエリウィンドウへ移ったり、接続し直したりすると、作成した一時テーブルは使えません。同じ接続で準備用SQLを実行してから、JOINの例を試してください。
まとめ
一致した組み合わせだけならINNER JOIN、左側の全行を残すならLEFT JOINです。まずは結果が3行と4行に分かれる理由を、顧客と注文の対応で確認してみましょう。
LEFT JOINで右側のデータを絞り込むときは、ONとWHEREの違いも大切です。「結合する相手を絞る」のか、「結合後の行を絞る」のかを意識すると、意図した結果を作りやすくなります。
結合の種類と条件の詳しい仕様は、Microsoft公式のJOIN解説も参考になります。
実行確認:2026年10月27日、SQL Server 2019 LocalDBで掲載例の結果を確認しました。画像はSSMS 22の実際の結果画面から、表の部分だけを撮影しています。



サイトアイコン-2-150x150.png)