- Day15 前半
- 「片方にしかいないデータも“落とさずに見る”」ための LEFT JOIN
- まずは INNER JOIN をもう一度、わざと“不便”に感じてみる
- LEFT JOIN の直感的なイメージ
- 「左側のテーブルを“主役”にして、右側をくっつける」
- LEFT JOIN の基本形
- INNER JOIN との違いは「LEFT」の一言だけ
- NULL が意味しているもの
- 「このユーザーには、対応する注文が存在しない」
- 「全員を出したいのか」「対応がある人だけでいいのか」を意識する
- セキュリティの視点から見る LEFT JOIN の意味
- 「“何もしていない人”を見落とさない」という大事さ
- 小さな練習イメージ
- Day15 前半のまとめ
Day15 前半
「片方にしかいないデータも“落とさずに見る”」ための LEFT JOIN
Day14 で学んだ INNER JOIN は、
「両方のテーブルに相手がいる行だけを残す結合」
でした。
Day15 のテーマ LEFT JOIN は、その逆方向の発想です。
「左側のテーブルにいる行は、相手がいなくても絶対に落とさない」
これが LEFT JOIN の本質です。
「ユーザーは全員見たい。でも、そのユーザーに注文があるかどうかも一緒に知りたい」
こういう場面で、LEFT JOIN が真価を発揮します。
まずは INNER JOIN をもう一度、わざと“不便”に感じてみる
前回と同じテーブルを使います。
users
id | name
---+-----------
1 | 山田太郎
2 | 佐藤花子
3 | 鈴木一郎
orders
id | user_id | amount
---+---------+-------
1 | 1 | 1200
2 | 1 | 3000
3 | 2 | 500
ここで、INNER JOIN を使って「誰がいくら買ったか」を出すとこうでした。
SELECT
u.id,
u.name,
o.amount
FROM users AS u
INNER JOIN orders AS o
ON u.id = o.user_id;
結果はこうなります。
id | name | amount
---+----------+-------
1 | 山田太郎 | 1200
1 | 山田太郎 | 3000
2 | 佐藤花子 | 500
ここで、よく見ると 鈴木一郎(id=3)がいない ことに気づきます。
理由はシンプルで、
orders.user_id = 3 の行が存在しない
→ users.id = 3 と結びつく注文がない
→ INNER JOIN の結果からは落ちる
からです。
「注文があるユーザーだけ見たい」ならこれでいいのですが、
「ユーザーは全員見たい。ついでに“注文があるかどうか”も知りたい」
というときには、INNER JOIN だと不便です。
ここで出てくるのが LEFT JOIN です。
LEFT JOIN の直感的なイメージ
「左側のテーブルを“主役”にして、右側をくっつける」
LEFT JOIN を一言でいうと、
「左側のテーブルの行は、絶対に全部残す結合」
です。
右側(結びつけられる相手)がいれば、その行をくっつける。
いなければ、右側の列は NULL にしてでも、左側の行を残す。
さっきの例でいうと、
users を左
orders を右
にして LEFT JOIN すると、
ユーザーは全員出す(山田・佐藤・鈴木)
そのうち、注文がある人には注文情報をくっつける
注文がない人は、注文の列が NULL になる
という動きになります。
LEFT JOIN の基本形
INNER JOIN との違いは「LEFT」の一言だけ
構文は INNER JOIN とほぼ同じです。
SELECT 列たち
FROM 左側のテーブル
LEFT JOIN 右側のテーブル
ON 結びつける条件;
さっきのテーブルで、「ユーザーは全員出しつつ、注文があれば金額も出したい」とすると、こう書きます。
SELECT
u.id,
u.name,
o.amount
FROM users AS u
LEFT JOIN orders AS o
ON u.id = o.user_id;
結果はこうなります。
id | name | amount
---+----------+-------
1 | 山田太郎 | 1200
1 | 山田太郎 | 3000
2 | 佐藤花子 | 500
3 | 鈴木一郎 | NULL
ここが INNER JOIN との決定的な違いです。
INNER JOIN
「両方に相手がいる組み合わせだけ」
LEFT JOIN
「左側は全員。右側は、いればくっつく。いなければ NULL」
「左側を主役にして、右側を“オプション”としてくっつける」
というイメージを持っておくと、かなり理解しやすくなります。
NULL が意味しているもの
「このユーザーには、対応する注文が存在しない」
LEFT JOIN の結果に出てくる NULL は、
単なる「値が入っていない」ではなく、
「結びつける相手が存在しなかった」
という意味を持っています。
さっきの結果でいうと、
鈴木一郎(id=3)は users にはいる
でも、orders に user_id=3 の行はない
→ LEFT JOIN の結果では、o.amount が NULL になる
この NULL を見て、
「このユーザーは、まだ一度も注文していないんだな」
と解釈できるわけです。
ここが、LEFT JOIN を「データの穴を見つける道具」として使うときの重要なポイントです。
「全員を出したいのか」「対応がある人だけでいいのか」を意識する
INNER JOIN と LEFT JOIN の使い分けは、
実はとてもシンプルな問いに帰ってきます。
「左側のテーブルの行は、全員出したいか?」
YES → LEFT JOIN
NO(対応がある人だけでいい) → INNER JOIN
さっきの例でいうと、
「注文があるユーザーだけ見たい」
→ INNER JOIN で十分
「ユーザーは全員見たい。注文があるかどうかも知りたい」
→ LEFT JOIN の出番
この「誰を主役にするか」を意識して JOIN を選ぶ癖をつけると、
結合の設計ミスがかなり減ります。
セキュリティの視点から見る LEFT JOIN の意味
「“何もしていない人”を見落とさない」という大事さ
セキュリティの観点でいうと、LEFT JOIN は
「何もしていない人」「何も起きていない場所」を見落とさない
ための道具になります。
たとえば、
全ユーザーは users にいる
ログイン履歴は login_logs にある
とします。
INNER JOIN で users × login_logs を結ぶと、
「ログインしたことがあるユーザー」しか出てきません。
でも、セキュリティ的には、
「一度もログインしていないユーザー」
「アカウントだけ作って放置されているユーザー」
も、ちゃんと把握しておきたいことがあります。
このとき、
SELECT
u.id,
u.name,
l.created_at
FROM users AS u
LEFT JOIN login_logs AS l
ON u.id = l.user_id;
のように LEFT JOIN すると、
ログインしたことがあるユーザー → created_at に値が入る
一度もログインしていないユーザー → created_at が NULL
という形で、「何もしていない人」が浮かび上がります。
Day15 前半の段階では、
LEFT JOIN は「左側を主役にして、“何もしていない人”も含めて全員を見る」ための結合
という感覚だけ持っておいてくれれば十分です。
小さな練習イメージ
頭の中で、次の日本語を SQL にしてみてください。
「全ユーザーを一覧で出しつつ、そのユーザーに注文があれば金額も一緒に出したい」
「ユーザーは全員出したい。注文がないユーザーは、金額が NULL になっていてもよい」
どちらも、
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
という骨格に、SELECT で欲しい列を足していく形になります。
Day15 前半のまとめ
INNER JOIN は「両方に相手がいる行だけ」を残すのに対し、LEFT JOIN は「左側の行は絶対に残す」。
LEFT JOIN の結果に出てくる NULL は、「右側に対応する行が存在しない」という意味を持つ。
「左側のテーブルの全員を出したいか?」が、INNER JOIN と LEFT JOIN を選ぶ基準になる。
LEFT JOIN は、「何もしていない人」「まだ一度も行動していないユーザー」を見落とさないための道具としても重要。
後半では、
LEFT JOIN と WHERE の組み合わせで「本当に“注文なしユーザー”だけを抽出する」方法、
LEFT JOIN した結果に対して GROUP BY / HAVING を使うパターン、
「アクティブユーザー/非アクティブユーザー」を切り分ける実務寄りの例
まで踏み込んで、LEFT JOIN の使いどころをさらに立体的にしていきます。
