SQLで「値によって表示を変えたい」「条件に応じて計算式を切り替えたい」という場面は、実務でも頻繁に登場します。そのような条件分岐を実現するのがCASE式です。プログラミング言語のif文に相当するCASE式には、値の等値比較に特化した「単純CASE」と、任意の条件を書ける「検索CASE」の2種類があります。本記事では両者の構文の違いと使い分け、SELECT・ORDER BY・GROUP BY内での活用例、そしてNULLを扱う際の注意点を、実際のSQLを交えて解説します。
このシナリオで考える
今回は売上管理システムを例にします。商品の注文を管理する orders テーブルを使います。status 列には 'pending'(処理中)・'shipped'(発送済み)・'cancelled'(キャンセル)の3種類の値が入ります。
CREATE TABLE orders (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
product VARCHAR(50) NOT NULL,
category VARCHAR(20) NOT NULL,
amount DECIMAL(10,2) NOT NULL,
status VARCHAR(20) NOT NULL,
created_at DATE NOT NULL
);
INSERT INTO orders (user_id, product, category, amount, status, created_at) VALUES
(1, 'ノートPC', 'electronics', 120000, 'shipped', '2026-05-01'),
(2, 'マウス', 'electronics', 3500, 'shipped', '2026-05-02'),
(3, 'Tシャツ', 'apparel', 2800, 'pending', '2026-05-03'),
(4, 'デスク', 'furniture', 45000, 'cancelled', '2026-05-04'),
(5, 'キーボード', 'electronics', 8000, 'shipped', '2026-05-05'),
(6, 'ジャケット', 'apparel', 12000, 'pending', '2026-05-06');
単純CASEの書き方
単純CASE(Simple CASE)は、1つの値を複数の選択肢と等値比較します。構文は以下の通りです。
CASE <比較対象>
WHEN <値1> THEN <結果1>
WHEN <値2> THEN <結果2>
...
[ELSE <それ以外の結果>]
END
status 列の英語値を日本語に変換してみましょう。
SELECT
product,
amount,
CASE status
WHEN 'shipped' THEN '発送済み'
WHEN 'pending' THEN '処理中'
WHEN 'cancelled' THEN 'キャンセル'
ELSE '不明'
END AS status_ja
FROM orders;
| product | amount | status_ja |
|---|---|---|
| ノートPC | 120000.00 | 発送済み |
| マウス | 3500.00 | 発送済み |
| Tシャツ | 2800.00 | 処理中 |
| デスク | 45000.00 | キャンセル |
| キーボード | 8000.00 | 発送済み |
| ジャケット | 12000.00 | 処理中 |
単純CASEは「1つの列を複数の固定値と照合する」場面に向いています。ELSE を書いておくと、どの WHEN にも一致しなかった場合の結果を明示できます。ELSE を省略すると NULL が返るため、意図しない NULL を防ぐために書いておくのが安全です。
検索CASEの書き方
検索CASE(Searched CASE)は比較対象を固定せず、WHEN 節に任意の条件式を書けます。
CASE
WHEN <条件1> THEN <結果1>
WHEN <条件2> THEN <結果2>
...
[ELSE <それ以外の結果>]
END
amount の金額に応じてランクを付けてみましょう。
SELECT
product,
amount,
CASE
WHEN amount >= 100000 THEN 'S'
WHEN amount >= 10000 THEN 'A'
WHEN amount >= 5000 THEN 'B'
ELSE 'C'
END AS rank
FROM orders;
| product | amount | rank |
|---|---|---|
| ノートPC | 120000.00 | S |
| マウス | 3500.00 | C |
| Tシャツ | 2800.00 | C |
| デスク | 45000.00 | A |
| キーボード | 8000.00 | B |
| ジャケット | 12000.00 | A |
WHEN の条件は上から順に評価され、最初に真になった節の結果が返ります。範囲比較・複合条件(AND/OR)・サブクエリも書けるため、単純CASEより表現力が高いです。
単純CASEと検索CASEの使い分け
2種類のCASEをまとめると以下のようになります。
| 単純CASE | 検索CASE | |
|---|---|---|
| 比較方法 | 等値比較(=)固定 | 任意の条件式 |
| NULLの比較 | 不可(常に不一致) | IS NULLで可能 |
| 範囲比較 | 不可 | 可能(>=、BETWEENなど) |
| 複合条件 | 不可 | AND/ORで可能 |
| 可読性 | 同じ列の比較には高い | 複雑な条件にも対応 |
迷ったときは検索CASEを選べば間違いありません。単純CASEは「同じ列の値を複数の固定値と比較する」場面に限定すると、コードが読みやすくなります。
CASE式を使える場所
ORDER BYでカスタム並び順を作る
文字列の辞書順とは異なる業務上の順序で並べ替えたいとき、CASE式が役立ちます。status を「処理中 → 発送済み → キャンセル」の優先順で並べる例です。
SELECT product, status
FROM orders
ORDER BY
CASE status
WHEN 'pending' THEN 1
WHEN 'shipped' THEN 2
WHEN 'cancelled' THEN 3
ELSE 99
END;
インデックスや追加カラムなしに、任意のカスタム順を実現できます。
GROUP BYで集計軸を動的に変える
category をおおまかに「デジタル」と「その他」にまとめて集計してみましょう。
SELECT
CASE category
WHEN 'electronics' THEN 'デジタル'
ELSE 'その他'
END AS category_group,
SUM(amount) AS total_amount,
COUNT(*) AS order_count
FROM orders
GROUP BY
CASE category
WHEN 'electronics' THEN 'デジタル'
ELSE 'その他'
END;
| category_group | total_amount | order_count |
|---|---|---|
| デジタル | 131500.00 | 3 |
| その他 | 59800.00 | 3 |
GROUP BY にエイリアス(category_group)を書くとMySQLのバージョンによってはエラーになることがあります。CASE式をそのまま繰り返すか、サブクエリで包む方が確実です。
よくある落とし穴
単純CASEでNULLを比較しようとする
単純CASEの WHEN は内部で等値演算子(=)を使います。NULL = NULL の結果は TRUE でも FALSE でもなく UNKNOWN になるため、WHEN NULL は決して一致しません。
-- NG: status が NULL でも 'データなし' にならない
CASE status
WHEN NULL THEN 'データなし' -- 永遠にマッチしない
WHEN 'shipped' THEN '発送済み'
ELSE 'その他'
END
NULLを判定するには検索CASEと IS NULL を組み合わせます。
-- OK: IS NULL で正しく検出できる
CASE
WHEN status IS NULL THEN 'データなし'
WHEN status = 'shipped' THEN '発送済み'
ELSE 'その他'
END
ELSEを省略してNULLが混入する
どの WHEN にも一致しなかった場合、ELSE がなければ NULL が返ります。この NULL が SUM や文字列連結に流れ込むと、予期しない結果になります。数値を期待する場所では ELSE 0、文字列なら ELSE '' を明示するのが安全な習慣です。
-- キャンセル分を除外して合計する例
SELECT SUM(
CASE
WHEN status = 'cancelled' THEN 0
ELSE amount
END
) AS effective_total
FROM orders;
THEN節の型を混在させる
1つのCASE式の中でTHEN節の型が混在すると、MySQLは暗黙の型変換を行います。数値と文字列が混在すると全体が文字列として扱われることがあります。型を統一するか、CAST() で明示的に変換することをお勧めします。
-- 意図しない型変換の例
CASE status
WHEN 'shipped' THEN 100 -- 数値
ELSE '未確定' -- 文字列 → 全体が文字列になる可能性
END
-- 型を統一した例
CASE status
WHEN 'shipped' THEN CAST(100 AS CHAR)
ELSE '未確定'
END
まとめ
CASE式には「単純CASE(等値比較)」と「検索CASE(任意条件)」の2種類があります。SELECT列・ORDER BY・GROUP BYなど式が書ける場所であれば使えます。NULLの比較には IS NULL を用いる検索CASEが必要で、ELSE の省略は意図しないNULLの原因になります。この3点を押さえておけば、CASE式を安全に活用できます。
参考リンク
アイキャッチ画像: Photo by Jantine Doornbos on Unsplash
