CASE式で条件分岐
分類のためだけに、全行をアプリへ運んでいる
「注文を金額帯で分けたい」と言われたとき、注文を全部取り出してアプリ側で if を書く手はあります。ただ、その分類で並べ替えたい、分類ごとに数えたい、と話が伸びるたびに、集計の処理まで自分で書く羽目になります。
分類そのものを SQL の中で書けるのが CASE 式です。
SQL クエリ
SELECT
id,
total,
CASE
WHEN total >= 10000 THEN 'A'
WHEN total >= 5000 THEN 'B'
ELSE 'C'
END AS rank
FROM orders;WHEN 条件 THEN 返す値 を必要なだけ並べ、最後を END で閉じます。CASE は文ではなく式なので、1 つの値が書ける場所ならどこにでも置けます。
順番を入れ替えると、答えが変わる
CASE は WHEN を上から順に見て、最初に当てはまったところの値を返します。当てはまった時点で、残りの WHEN は見ません。
だから上の例で total >= 5000 を先に書くと、12000 円の注文が B になります。5000 以上という条件に先に当たってしまい、10000 以上の行にたどり着かないからです。範囲で区切る分岐は、大きいほうから書くか小さいほうから書くか、どちらかに必ず統一します。
ELSE を書かないと、そこだけ空になる
どの WHEN にも当てはまらなかった行は、ELSE があればその値になり、なければ NULL になります。
SQL クエリ
CASE
WHEN status = 'paid' THEN '入金済み'
WHEN status = 'sent' THEN '発送済み'
ELSE '不明'
END AS status_label想定外の値など来ないと思っていても、受け皿を書いておくと「不明が 3 件ある」という形で異常に気づけます。ELSE を省くと、その 3 件は空欄として静かに紛れ込みます。
2 つの条件を、1 回のクエリで数える
CASE は集計関数の中にも書けます。
SQL クエリ
SELECT
SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_count,
SUM(CASE WHEN status = 'canceled' THEN 1 ELSE 0 END) AS canceled_count
FROM orders;条件に合う行だけ 1 を足しているので、状態ごとの件数が 1 行に横並びで出ます。WHERE で絞る書き方だと 1 つの条件につき 1 回クエリを投げることになりますが、この形なら何種類でも横に並べられます。ダッシュボードに並ぶ数字は、たいていこの作り方をしています。
テーブル構造
CREATE TABLE members (
id INT PRIMARY KEY,
name VARCHAR(30) NOT NULL,
age INT NOT NULL
);
INSERT INTO members (id, name, age) VALUES
(1, '田中太郎', 15),
(2, '佐藤花子', 22),
(3, '鈴木一郎', 45),
(4, '高橋美咲', 60),
(5, '伊藤健太', 72),
(6, '渡辺涼子', 30);期待される出力
| id | name | age | generation |
|---|---|---|---|
| 1 | 田中太郎 | 15 | 未成年 |
| 2 | 佐藤花子 | 22 | 現役 |
| 3 | 鈴木一郎 | 45 | 現役 |
| 4 | 高橋美咲 | 60 | シニア |
| 5 | 伊藤健太 | 72 | シニア |
| 6 | 渡辺涼子 | 30 | 現役 |