たぶん、もうプログラミング言語で条件式(if-elseやswitch-caseみたいなやつ)に触れたことあるよね。SQLにも条件を扱うためのツールがあるんだ。それがCASE式。クエリの中で「もし条件が合えばこうする、違ったらああする」って判断できる。
CASE構文は、特にNULL値が入ってるデータを扱うときに超便利。シンタックスはカンタン、まるでチートシートみたい。見てみよう:
CASE
WHEN 条件1 THEN 結果1
WHEN 条件2 THEN 結果2
...
ELSE デフォルト結果
END
ロジカルでしょ?「もしこうならこれ、違ったらあれ」って感じ。覚えやすいよ。「WHEN=もし、THEN=何する、ELSE=どれも違ったらどうする」ってノリ。
例: 商品の分類
たとえばproductsテーブルがあって、priceカラムがあるとする。その値で商品をグループ分けしたいとき。
| id | name | price |
|---|---|---|
| 1 | Magic Wand | 120 |
| 2 | Potion Set | 45 |
| 3 | Crystal Ball | 75 |
| 4 | Enchanted Map | NULL |
| 5 | Broomstick | 99 |
| 6 | Spell Book | 180 |
SELECT
name AS product_name,
price,
CASE
WHEN price IS NULL THEN '不明'
WHEN price < 50 THEN 'お手頃'
WHEN price BETWEEN 50 AND 100 THEN '標準'
ELSE 'プレミアム'
END AS price_category
FROM products;
ここで何が起きてる?
- 商品の
priceがNULLなら、カテゴリは'不明'になる。 - 価格が50未満なら「お手頃」
'お手頃'。 - 50~100なら「標準」
'標準'。 - それ以外は「プレミアム」
'プレミアム'。
結果はこんな感じ:
| product_name | price | price_category |
|---|---|---|
| Magic Wand | 120 | プレミアム |
| Potion Set | 45 | お手頃 |
| Crystal Ball | 75 | 標準 |
| Enchanted Map | NULL | 不明 |
| Broomstick | 99 | 標準 |
| Spell Book | 180 | プレミアム |
SQLはまるで魔法使いみたいにproductsテーブルの行を読んで、1つずつうまく分類してくれる。
CASE WHENでNULLを扱う
データに抜けがある(やあ、NULL!)ことはよくあるし、何かで埋めたいことも多い。たとえばusersテーブルにemailカラムがあって、メールアドレスがないユーザーには'未設定'って表示したい場合。
| user_id | name | |
|---|---|---|
| 1 | Alex Lin | alex@example.com |
| 2 | Maria Chi | NULL |
| 3 | Anna Song | anna@magic.com |
| 4 | Otto Art | NULL |
| 5 | John Smith | john@wizard.org |
SELECT
user_id,
name,
CASE
WHEN email IS NULL THEN '未設定'
ELSE email
END AS email_address
FROM users;
| user_id | name | email_address |
|---|---|---|
| 1 | Alex Lin | alex@example.com |
| 2 | Maria Chi | 未設定 |
| 3 | Anna Song | anna@magic.com |
| 4 | Otto Art | 未設定 |
| 5 | John Smith | john@wizard.org |
SQLはここでシャーマンみたいに、半分空っぽの行を生き返らせてくれる。emailがなければ'未設定'に、あればそのまま。
数字を使った条件式
値の置き換えだけじゃなく、新しいロジックを作りたいときもある。たとえばstudentsテーブルにscore(点数)とnameカラムがある場合。
| name | score |
|---|---|
| Alex Lin | 95 |
| Maria Chi | 82 |
| Anna Song | 48 |
| Otto Art | NULL |
| John Smith | 67 |
| Zoe Black | 30 |
学生を「優秀」「合格」「不合格」みたいに点数で評価したいとき。
SELECT
name AS student_name,
score,
CASE
WHEN score IS NULL THEN '点数なし'
WHEN score >= 90 THEN '優秀'
WHEN score >= 50 THEN '合格'
ELSE '不合格'
END AS performance_category
FROM students;
| student_name | score | performance_category |
|---|---|---|
| Alex Lin | 95 | 優秀 |
| Maria Chi | 82 | 合格 |
| Anna Song | 48 | 不合格 |
| Otto Art | NULL | 点数なし |
| John Smith | 67 | 合格 |
| Zoe Black | 30 | 不合格 |
SQLは厳しい試験官みたいに学生を振り分けてくれる。90点以上は「優秀」、50点以上は「合格」。足りなければ…まあ、わかるよね。
グループ化とNULLの処理
データのグループ化もCASE WHENが大活躍。たとえばordersテーブルで、NULLステータスも含めて注文数を数えたいとき。
| order_id | status |
|---|---|
| 1 | Completed |
| 2 | Pending |
| 3 | NULL |
| 4 | Shipped |
| 5 | Completed |
| 6 | NULL |
| 7 | Pending |
| 8 | Completed |
| 9 | Shipped |
| 10 | NULL |
SELECT
CASE
WHEN status IS NULL THEN 'ステータスなし'
ELSE status
END AS order_status,
COUNT(*)
FROM orders
GROUP BY
CASE
WHEN status IS NULL THEN 'ステータスなし'
ELSE status
END;
このクエリはNULLの空ステータスを'ステータスなし'に置き換えて、各グループの注文数を数えてくれる。結果はこんな感じ:
| order_status | count |
|---|---|
| Completed | 3 |
| Pending | 2 |
| Shipped | 2 |
| ステータスなし | 3 |
実践ケース:「CASE WHEN」のトリック
例1: NULLを考慮したソート
ときどきNULL値をリストの最初や最後に表示したいことがある。たとえばタスク一覧で、優先度が高いものを上に、優先度なし(NULL)は下にしたい場合とか。
| task_id | task_name | priority |
|---|---|---|
| 1 | Fix bugs | 1 |
| 2 | Update documentation | 3 |
| 3 | Plan sprint | NULL |
| 4 | Code review | 2 |
| 5 | Organize meeting | NULL |
| 6 | Deploy release | 1 |
SELECT
task_name,
priority,
CASE
WHEN priority IS NULL THEN 1
ELSE 0
END AS priority_sort
FROM tasks
ORDER BY priority_sort ASC, priority ASC;
ここでは「仮想」カラムpriority_sortを追加して、NULL値を一番下に、それ以外は昇順で並べてる。
| task_name | priority | priority_sort |
|---|---|---|
| Deploy release | 1 | 0 |
| Fix bugs | 1 | 0 |
| Code review | 2 | 0 |
| Update documentation | 3 | 0 |
| Plan sprint | NULL | 1 |
| Organize meeting | NULL | 1 |
例2: NULLを考慮した計算
今度はordersテーブルで、discountカラムがNULL(割引なし)かもしれない場合の合計金額計算。
| order_id | total_price | discount |
|---|---|---|
| 101 | 100 | 10 |
| 102 | 200 | NULL |
| 103 | 150 | 15 |
| 104 | 120 | NULL |
| 105 | 80 | 5 |
NULLは0に置き換えて計算が壊れないようにする。
SELECT
order_id,
total_price,
discount,
total_price -
CASE
WHEN discount IS NULL THEN 0
ELSE discount
END AS final_price
FROM orders;
| order_id | total_price | discount | final_price |
|---|---|---|---|
| 101 | 100 | 10 | 90 |
| 102 | 200 | NULL | 200 |
| 103 | 150 | 15 | 135 |
| 104 | 120 | NULL | 120 |
| 105 | 80 | 5 | 75 |
このCASEの魔法トリックで、NULLが計算を壊す心配なし!
orderid = 101: discount = 10. finalprice = 100 - 10 = 90。orderid = 102: discount = NULL。CASEは0を返す。finalprice = 200 - 0 = 200。orderid = 103: discount = 15. finalprice = 150 - 15 = 135。orderid = 104: discount = NULL。CASEは0を返す。finalprice = 120 - 0 = 120。orderid = 105: discount = 5. finalprice = 80 - 5 = 75。
例3: ユーザーステータスの表示
日常的によくあるのが、ユーザーのステータス(たとえば「アクティブ」や「待機中」)やデータなしを表示したいケース。たとえばusersテーブルにlast_login(最終ログイン日)がある場合。
| user_id | name | last_login |
|---|---|---|
| 1 | Alex Lin | 2024-12-10 |
| 2 | Maria Chi | 2025-04-20 |
| 3 | Anna Song | NULL |
| 4 | Otto Art | 2025-05-01 |
| 5 | Liam Park | 2025-05-25 |
SELECT
user_id,
name,
CASE
WHEN last_login IS NULL THEN '一度もログインなし'
WHEN last_login < CURRENT_DATE - INTERVAL '30 days' THEN '非アクティブ'
ELSE 'アクティブ'
END AS user_status
FROM users;
このクエリで管理画面が生き生きする。ログイン履歴なしは「一度もログインなし」、長期間ログインしてない人は「非アクティブ」、それ以外は「アクティブ」!
| user_id | name | user_status |
|---|---|---|
| 1 | Alex Lin | 非アクティブ |
| 2 | Maria Chi | 非アクティブ |
| 3 | Anna Song | 一度もログインなし |
| 4 | Otto Art | 非アクティブ |
| 5 | Liam Park | アクティブ |
よくあるミスとその回避法
ELSEの書き忘れ: ELSEを書かないと、どの条件にも当てはまらなかったときSQLはNULLを返す。それってたぶん望んでないよね。だから全部カバーしたつもりでもELSEは明示的に書こう。
CASE
WHEN condition THEN result
-- ELSE 'デフォルト値' -- 忘れずに!
END
カッコなしの複雑な条件: ANDやOR、NOTを複数使うときは必ずカッコを使おう。カッコなしだとクエリが「勘違い」しちゃうかも。
CASE
WHEN (column1 IS NOT NULL AND column2 > 5) THEN '有効'
ELSE '無効'
END
NULLの扱い: NULLは何かと等しくなることは絶対にない(=はダメ)。たとえば:
CASE
WHEN column = NULL THEN 'ダメ!' -- エラー!
WHEN column IS NULL THEN '正解!' -- これが正しい。
END
GO TO FULL VERSION