SQLite | ゼロからはじめるSQL、30日で習得するSQLite:検索力強化 - Day15 結合②

SQL SQLite
スポンサーリンク

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 の使いどころをさらに立体的にしていきます。

タイトルとURLをコピーしました