INNER JOINの基本
JOIN を書いて実行したら、元のテーブルより行数が減っていた。結合を覚えたばかりの頃によくある戸惑いです。減るのは仕様で、しかもその減り方こそが INNER JOIN の正体です。
相手が見つからなかった行は、黙って消える
SQL クエリ
-- 在庫レコードがある商品だけが並ぶ
SELECT p.name, s.quantity
FROM products p
INNER JOIN stocks s
ON p.id = s.product_id;在庫テーブルに行が無い商品、たとえば登録したての新商品や廃番品は、この結果には出てきません。INNER JOIN が返すのは「両方のテーブルに相手が見つかった行だけ」だからです。エラーも警告も出ないので、件数を数えて初めて気づきます。
なお INNER は省略できて、JOIN とだけ書いても内部結合になります。
逆に、行が増えることもあります。1 つの商品に在庫レコードが倉庫ごとに 2 行あれば、その商品は結果に 2 行並びます。ON で結ぶ相手が 1 行とは限らない、という当たり前のことが、結合した瞬間に見えにくくなります。
NULL は誰とも一致しない
結合に使う列が NULL の行も、同じように消えます。NULL は「値が無い」という印なので、NULL = NULL は真になりません。担当者が未設定の商品や、部署が決まっていない社員は、内部結合ではまとめて落ちます。
落としたくない場合は、片側を全部残す結合に切り替えます。逆に「実績のあるものだけを並べたい」売上ランキングのような用途では、相手のいない行が自動的に消えてくれるこの性質がそのまま役に立ちます。
ON は結合の条件、WHERE は結合後の絞り込み
SQL クエリ
SELECT p.name, s.quantity
FROM products p
JOIN stocks s ON p.id = s.product_id
WHERE s.quantity > 0;ON に書くのは「どの行とどの行を合わせるか」、WHERE に書くのは「合わせた結果から何を残すか」です。内部結合ではどちらに書いても結果が同じになることが多いのですが、片側を全部残す結合では結果が変わります。役割で分けて書く習慣を先に付けておくと、あとで混乱しません。
SQL クエリ
-- 結合の前後で件数を比べる
SELECT COUNT(*) FROM products;
SELECT COUNT(*) FROM products p JOIN stocks s ON p.id = s.product_id;結合したら必ず件数を確かめてください。増えていれば
ONに書いた列が一対多になっていて、減っていれば相手が見つからない行かNULLが混じっています。
テーブル構造
CREATE TABLE departments (
id INT PRIMARY KEY,
name VARCHAR(30)
);
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(30),
department_id INT,
salary INT
);
INSERT INTO departments VALUES
(1, '営業'),
(2, '開発'),
(3, '人事'),
(4, '総務');
INSERT INTO employees VALUES
(101, '田中', 1, 350000),
(102, '鈴木', 2, 420000),
(103, '佐藤', 2, 380000),
(104, '高橋', 3, 320000),
(105, '山田', NULL, 300000);期待される出力
| employee_id | employee_name | department_name | salary |
|---|---|---|---|
| 101 | 田中 | 営業 | 350000 |
| 102 | 鈴木 | 開発 | 420000 |
| 103 | 佐藤 | 開発 | 380000 |
| 104 | 高橋 | 人事 | 320000 |