SQL SELECT文のテンプレ8選 ― 集計・JOIN・サブクエリですぐ使えるパターン
業務でよく使うSQLのSELECT文テンプレを8パターン収録。GROUP BY、JOIN、CASE、ウィンドウ関数までコピペで動かせます。
SQLは「毎回ちょっとずつ違う書き方になる」言語の代表選手だと思ってます(- -;
「あれ、JOINの構文どっちだっけ…」とリファレンスを開く時間、累計するとそこそこ消えてるはず。
そこで自分の引き出し用に、業務頻出パターンをここに集約しました。
シーン別に「これに当てはまる」と即座に判断できると、SQLを書く時間がそれなりに短くなります。
気をつけたいのは下記の3点。
- SELECT * は禁止(明示的にカラム指定)
- LIMIT忘れに注意(特に開発時の検索系)
- インデックスを意識(WHERE句の条件カラムにINDEX)
下記はPostgreSQL想定で書いてますが、MySQLでもほぼそのまま動くようにしています!
1. 基本のWHERE句で絞り込み
用途: 特定条件のレコードを取得する基本パターン
SELECT
id,
name,
email,
created_at
FROM
users
WHERE
status = 'active'
AND created_at >= '2026-01-01'
ORDER BY
created_at DESC
LIMIT 100;
基本パターンは **「カラム明示」+「WHERE条件」+「ORDER BY」+「LIMIT」**の4点セットでOK。
SELECT * は便利なんですが、運用に入ってから問題に気付くケースがけっこうあって、自分も以前 SELECT * で取った結果をそのまま JSON レスポンスにしていたら、ある日カラム追加された途端にPIIっぽい項目がそのままAPIから漏れかけたことがあるんですよね…。それ以来、カラム明示はクセにしています。
2. GROUP BY で集計
用途: カテゴリ別の件数・合計を出すとき
SELECT
category,
COUNT(*) AS total_count,
SUM(amount) AS total_amount,
AVG(amount) AS avg_amount
FROM
orders
WHERE
created_at >= '2026-01-01'
GROUP BY
category
ORDER BY
total_count DESC;
集計は COUNT、SUM、AVG をAS でわかりやすいエイリアスにするのがコツ。
レポート用のSQLだと特に「何の数字か」が分からないと困るので、命名は丁寧に(^^!
3. INNER JOIN で関連テーブル結合
用途: 関連テーブルから情報を取得するとき
SELECT
u.id AS user_id,
u.name AS user_name,
o.id AS order_id,
o.amount AS order_amount
FROM
users u
INNER JOIN
orders o ON u.id = o.user_id
WHERE
u.status = 'active'
ORDER BY
o.created_at DESC;
JOINは **テーブル別名(u, o)**で短く書くと読みやすいです。
INNER JOINだと両テーブルにマッチするレコードだけが取れるので、片方にしか無いデータも欲しいときは LEFT JOIN を使います(^^b
4. LEFT JOIN で欠損も含める
用途: 注文のないユーザーも含めて取得するとき
SELECT
u.id,
u.name,
COUNT(o.id) AS order_count
FROM
users u
LEFT JOIN
orders o ON u.id = o.user_id
GROUP BY
u.id, u.name
ORDER BY
order_count DESC;
LEFT JOIN + COUNT で「ゼロ注文ユーザー」も含めた集計ができます。
INNER JOINだと注文ゼロのユーザーが消えるので、リテンション分析などはLEFT必須です(^^!
5. EXISTSでサブクエリ
用途: 「ある条件を満たす関連レコードがあるか」で絞り込む
SELECT
u.id,
u.name,
u.email
FROM
users u
WHERE
EXISTS (
SELECT 1
FROM orders o
WHERE o.user_id = u.id
AND o.amount >= 10000
);
EXISTSは **「該当データが1件でもあれば」**という条件チェック専用。
JOINで集計するよりパフォーマンスが良いケースが多いので、データ量が大きい時はEXISTSが有利になるパターンが多いです(^^b
6. CASE WHEN で条件分岐
用途: 値に応じて別のカテゴリを付与するとき
SELECT
id,
name,
amount,
CASE
WHEN amount >= 100000 THEN '高額'
WHEN amount >= 10000 THEN '中額'
ELSE '少額'
END AS amount_category
FROM
orders
ORDER BY
amount DESC;
CASE WHENは **「複数条件の分岐ラベリング」**が圧倒的に便利。
レポート集計用に「金額帯別」「年齢層別」など作るときに重宝します(^^!
7. ウィンドウ関数で順位付け
用途: グループごとの順位を取得するとき
SELECT
user_id,
order_id,
amount,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY amount DESC
) AS rank_in_user
FROM
orders;
ウィンドウ関数は **「グループ内の順位や移動平均」**を出すのに重宝します。
ROW_NUMBER() の他に RANK()、DENSE_RANK()、SUM() OVER (...) などバリエーション豊富です(^^b
8. ページネーション(OFFSET と LIMIT)
用途: 検索結果を1ページ20件ずつ取得するとき
SELECT
id,
name,
created_at
FROM
users
WHERE
status = 'active'
ORDER BY
created_at DESC
LIMIT 20 OFFSET 40;
OFFSETは **「最初の40件をスキップして、次の20件」**という意味。
ただしOFFSETが大きくなるとパフォーマンスが落ちるので、巨大データなら カーソルベースのページネーション(WHERE created_at < ? )への移行を検討します(- -;