doodle-on-web

自分で調べたことや、仕事の中で質問されたことなどをまとめています。

SQLのSELECTで固定値・計算列を返す方法|CASE式で区分コードを日本語名に変換する実践ガイド

スポンサーリンク

「区分コードの 012 を、アプリ側のコードで 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の値を返します。どれにも当てはまらなければELSEELSEを省くと該当しない行は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 0status = 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 ENDCOALESCE(discount, 0)で書けます。
  • THENの型を揃える:各THENの返す型がバラバラだと、処理系によってエラーや暗黙変換が起きます。
  • WHENの順序が意味を持つ:検索CASEは上から評価されるので、厳しい条件を先に書きます。

まとめ

  • SELECTには列だけでなく、固定値や計算式も書ける
  • 固定値の列はUNION ALLの区別やレポート出力で活躍する
  • CASE式は検索CASE(範囲判定)と単純CASE(等価変換)を使い分ける
  • SUM(CASE ...)COUNT(CASE ...)で条件付き集計、NULL置換はCOALESCE

区分コードの変換をSQL側に寄せると、アプリのコードがぐっとシンプルになります。まずは手元のstatus列を、日本語名の集計レポートに変えるところから試してみてください。

あわせてチェック