「区分コードの 0・1・2 を、アプリ側のコードで if を書いて『未処理・処理中・完了』に変換していた」——そんな処理は、SQLのSELECTだけで完結できます。テーブルの列をそのまま返すだけでなく、固定値や計算結果、条件で切り替えた値まで返せるからです。
この記事では、固定値(定数列)とCASE式を使って、コードを日本語名に変換してレポート出力するところまでを、実行結果のイメージ付きで解説します。
SELECTには「式」なら何でも書ける
SELECTの後ろには、列名だけでなく式(expression)を書けます。列名はその一種にすぎません。
SELECT id, price, price * 1.1 AS price_with_tax, -- 計算列 'JPY' AS currency -- 固定値の列 FROM products;
currency列がテーブルに存在しなくても、全行に'JPY'を持つ列が返ります。ポイントはASで別名を付けること。付けないと列名が処理系依存になるので、習慣にしておくと安全です。
固定値(定数列)を返す
固定値が特に活きるのがUNION ALLです。どのテーブル由来の行かを列で区別できます。
SELECT id, amount, 'online' AS channel FROM online_orders UNION ALL SELECT id, amount, 'store' AS channel FROM store_orders;
こうしておけば、後段でGROUP BY channelするだけでチャネル別の売上を集計できます。データソースを1つにまとめる前処理として便利です。
CASE式で条件によって値を切り替える
行の内容に応じて返す値を変えたいときはCASE式。SQLにおけるif-elseです。
検索CASE(範囲判定に使う)
SELECT id, score, CASE WHEN score >= 80 THEN 'A' WHEN score >= 60 THEN 'B' ELSE 'C' END AS grade FROM exam_results;
上から順に評価し、最初に真になったWHENの値を返します。どれにも当てはまらなければELSE。ELSEを省くと該当しない行はNULLになります。
単純CASE(等価変換に使う)
区分コードを名称に変える等価比較なら、短く書けます。
SELECT id, CASE status WHEN 0 THEN '未処理' WHEN 1 THEN '処理中' WHEN 2 THEN '完了' ELSE '不明' END AS status_name FROM tasks;
CASE status WHEN 0 は status = 0 と同じ意味です。ただし等価比較専用で、>=のような範囲条件には使えません。
実践:区分マスタなしでコードを名称化してレポート出力
ここが本題です。マスタテーブルを用意しなくても、CASE式だけでコードを日本語名にして、そのまま集計レポートを作れます。次のtasksテーブルを例にします。
| id | status |
|---|---|
| 1 | 2 |
| 2 | 0 |
| 3 | 2 |
| 4 | 1 |
SELECT CASE status WHEN 0 THEN '未処理' WHEN 1 THEN '処理中' WHEN 2 THEN '完了' ELSE '不明' END AS 状態, COUNT(*) AS 件数 FROM tasks GROUP BY status;
実行結果はこうなります。コードのままでは読めなかった集計が、そのまま人に見せられるレポートになります。
| 状態 | 件数 |
|---|---|
| 未処理 | 1 |
| 処理中 | 1 |
| 完了 | 2 |
CASEで集計を横に展開する(条件付き集計)
CASE式はSUMと組み合わせると、1回のスキャンで複数の集計を横並びにできます。ピボット集計の基本形です。
SELECT SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) AS done_count, SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS doing_count FROM tasks;
先ほどのtasksなら、結果は横一列に返ります。
| done_count | doing_count |
|---|---|
| 2 | 1 |
なお、これはCOUNTでも書けます。COUNTはNULLを数えないので、ELSEを省くだけで同じ結果になります。
COUNT(CASE WHEN status = 2 THEN 1 END) AS done_count
実務でハマる落とし穴
- 単純CASEで
NULLは判定できない:CASE status WHEN NULL THEN ...は効きません。= NULL扱いになり、常に偽になるためです。NULLを分岐したいなら検索CASEでWHEN status IS NULL THEN ...と書きます。 - NULL置換は
COALESCEが簡潔:CASE WHEN discount IS NULL THEN 0 ELSE discount ENDはCOALESCE(discount, 0)で書けます。 - THENの型を揃える:各THENの返す型がバラバラだと、処理系によってエラーや暗黙変換が起きます。
- WHENの順序が意味を持つ:検索CASEは上から評価されるので、厳しい条件を先に書きます。
まとめ
SELECTには列だけでなく、固定値や計算式も書ける- 固定値の列は
UNION ALLの区別やレポート出力で活躍する - CASE式は検索CASE(範囲判定)と単純CASE(等価変換)を使い分ける
SUM(CASE ...)/COUNT(CASE ...)で条件付き集計、NULL置換はCOALESCE
区分コードの変換をSQL側に寄せると、アプリのコードがぐっとシンプルになります。まずは手元のstatus列を、日本語名の集計レポートに変えるところから試してみてください。