SQLは書けない人と書ける人で業務の生産性が10倍変わる分野だ。この記事では、初学者がつまずきやすい JOIN とサブクエリを軸に、2時間で実務レベルの感覚を掴むことを目標にする。PostgreSQLを前提にするが、MySQLでもほぼそのまま動く。
1. 準備 — サンプルテーブル
まず題材となる2つのテーブルを作る。
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
created_at TIMESTAMP DEFAULT now()
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id),
amount INT NOT NULL,
created_at TIMESTAMP DEFAULT now()
);
INSERT INTO users (name) VALUES ('Alice'), ('Bob'), ('Carol');
INSERT INTO orders (user_id, amount) VALUES (1, 1000), (1, 2000), (2, 1500);
Carolは注文を持たない。この前提で話を進める。
2. SELECTの基本
SQLは「どう取るか」ではなく「何が欲しいか」を書く言語だ。
SELECT id, name FROM users WHERE id = 1;
WHERE は行の絞り込み、SELECT は列の選択。この役割分担を最初にしっかり掴む。
3. INNER JOIN — 両方にある行だけ
INNER JOIN は両方のテーブルにマッチする行だけを返す。注文があるユーザーだけを知りたいときはこれ。
SELECT u.name, o.amount
FROM users u
INNER JOIN orders o ON o.user_id = u.id;
結果にCarolは出てこない。
4. LEFT JOIN — 左を必ず残す
注文がないユーザーも含めたい場合は LEFT JOIN を使う。右側に対応がなければNULLが埋まる。
SELECT u.name, o.amount
FROM users u
LEFT JOIN orders o ON o.user_id = u.id;
Carolの行が amount = NULL で出てくる。「未購入ユーザーを洗い出す」みたいな要件はこの形が定石だ。
5. GROUP BYと集計関数
ユーザーごとの購入総額を出す。
SELECT u.name, COALESCE(SUM(o.amount), 0) AS total
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id, u.name
ORDER BY total DESC;
GROUP BY 句には SELECT に出てくる非集計列を全部並べる。COALESCE でNULLを0に変換するのは頻出パターン。
6. サブクエリ — 一度の問い合わせで済ませる
平均より多く買っているユーザーを出す例。
SELECT name
FROM users
WHERE id IN (
SELECT user_id
FROM orders
GROUP BY user_id
HAVING SUM(amount) > (SELECT AVG(amount) FROM orders)
);
サブクエリは「結果セットを別のクエリの中で使う」と考える。慣れるとCTE(WITH句)の方が読みやすい場合も多い。
WITH user_totals AS (
SELECT user_id, SUM(amount) AS total FROM orders GROUP BY user_id
),
overall AS (
SELECT AVG(amount) AS avg_amount FROM orders
)
SELECT u.name, ut.total
FROM user_totals ut
JOIN users u ON u.id = ut.user_id, overall
WHERE ut.total > overall.avg_amount;
7. インデックスの基礎
検索性能を上げたい列にはインデックスを張る。ただし書き込みが遅くなるトレードオフがある。
CREATE INDEX idx_orders_user_id ON orders(user_id);
EXPLAIN ANALYZE で実行計画を確認しながら張る位置を決めるのが正解。
次のステップ
- ウィンドウ関数(
ROW_NUMBER、LAG)を覚える - 実行計画を読めるようになる
- N+1問題をアプリ側から解消するSQLを書けるようにする
SQLはアプリ開発の地力そのものだ。ここを固めるとバックエンドの選択肢が一気に広がる。