CASEはSQLクエリにif/then/elseのロジックを追加します。条件のリストを順に評価し、最初に真となった条件の結果を返します。例はPostgreSQL、MySQL、SQLite、SQL Serverで動作します。
CASEの使い方
CASEとENDの間にWHEN condition THEN resultのペアを並べます。CASEは最初に真となった条件の結果を返し、どれも真でなければELSEの値を返します。
CASE
WHEN condition THEN result
[WHEN condition THEN result ...]
[ELSE result]
END
SELECT
CASE
WHEN 72 >= 90 THEN 'Hot'
WHEN 72 >= 60 THEN 'Mild'
ELSE 'Cold'
END;
-- Output: Mild
すべての条件が同じ値との等価比較なら、より短い「単純」形式でその値を一度だけ指定できます。
SELECT
CASE 'amber'
WHEN 'green' THEN 'Go'
WHEN 'amber' THEN 'Slow down'
WHEN 'red' THEN 'Stop'
ELSE 'Unknown'
END;
-- Output: Slow down
実際のクエリでは条件でカラムを評価し、CASEは結果に新しいカラムを追加します。
-- Input: orders
-- order_id | amount
-- ---------+--------
-- 1001 | 45.00
-- 1002 | 100.00
-- 1003 | 349.99
-- 1004 | 1250.00
SELECT
order_id,
amount,
CASE
WHEN amount < 100 THEN 'Small'
WHEN amount < 500 THEN 'Medium'
ELSE 'Large'
END AS order_size
FROM orders;
-- Output:
-- order_id | amount | order_size
-- ---------+---------+-----------
-- 1001 | 45.00 | Small
-- 1002 | 100.00 | Medium
-- 1003 | 349.99 | Medium
-- 1004 | 1250.00 | Large
知っておくべきルール
-
最初に真となった条件が採用されます。条件は上から順に評価されるため、最も範囲の狭い条件を先に書きます。上の例では、
amount < 100が偽でamount < 500が真なので、100.00はMediumになります。順序を誤ると、小さい金額は本来の分岐に到達しません。
SELECT
CASE
WHEN 45 < 500 THEN 'Medium'
WHEN 45 < 100 THEN 'Small' -- can never win
ELSE 'Large'
END;
-- Output: Medium
- ELSEがない場合、一致しなければNULLになります。
SELECT CASE WHEN 72 >= 90 THEN 'Hot' END;
-- Output: NULL
-
NULLの判定にはIS NULLを使います。
NULL = NULLは真にならないため、単純形式ではNULLに一致させられません。検索形式を使ってください。
SELECT CASE NULL WHEN NULL THEN 'matched' ELSE 'no match' END;
-- Output: no match
SELECT CASE WHEN NULL IS NULL THEN 'matched' ELSE 'no match' END;
-- Output: matched
- すべての結果は互換性のある型である必要があります。1つのCASEで数値とテキストを混在させるとPostgreSQLやSQL Serverではエラーになるため、数値をキャストします。
-- Input: products
-- name | stock
-- -------------+------
-- Desk Lamp | 12
-- Office Chair | 0
SELECT name, CASE WHEN stock > 0 THEN stock ELSE 'Sold out' END AS stock
FROM products;
-- PostgreSQL: ERROR: invalid input syntax for type integer: "Sold out"
-- SQL Server: Conversion failed when converting the varchar value 'Sold out' to data type int.
-- (in MySQL, cast with CAST(stock AS CHAR))
SELECT
name,
CASE WHEN stock > 0 THEN CAST(stock AS VARCHAR(10)) ELSE 'Sold out' END AS stock
FROM products;
-- Output:
-- name | stock
-- -------------+---------
-- Desk Lamp | 12
-- Office Chair | Sold out
別の構文
条件が1つだけの場合は、より短い関数もよく使われます。
-- MySQL
SELECT IF(72 >= 60, 'Mild', 'Cold');
-- Output: Mild
-- SQL Server and SQLite
SELECT IIF(72 >= 60, 'Mild', 'Cold');
-- Output: Mild
よく使うCASE式の省略形となる関数が他に2つあります。COALESCE(nickname, name)はCASE WHEN nickname IS NOT NULL THEN nickname ELSE name ENDと同じ意味で、NULLIF(a, b)はCASE WHEN a = b THEN NULL ELSE a ENDと同じ意味です。前者についてはSQL COALESCEガイドをご覧ください。
MySQLにはストアドプロシージャ用のCASE文もあり、END CASEで終わります。この記事ではクエリで使うCASE式を扱います。
CASEはどんなときに使う?
CASEは式なので、値を書ける場所ならどこでも使えます。SELECTリスト、ORDER BYやGROUP BY、集計関数の中などです。WHEREでも使えますが、そこでは通常のANDやORの方がわかりやすいことが多いです。ほとんどの用途は次の5つのいずれかに当てはまります。
数値を範囲に分ける
数値を価格帯、注文サイズ、年齢層、成績などの区分に変換します。条件を尺度の一方の端からもう一方へ順に書けば、最初の一致が正しい区分になります。
コードをラベルに変換する
データベースには'A'、'ENG'、'gold'のような短いコードが保存されていますが、人が見たいのは言葉です。単純形式のCASEで各コードをラベルや割引率などの値に対応付け、想定外のコードはELSEで受け止めます。
一部の行だけを数える・合計する
COUNTやSUMの中にCASEを入れると、条件を満たす行だけを数えたり合計したりできます。COUNT(CASE WHEN ... THEN 1 END)が機能するのは、一致しない行がNULLになり、COUNTがNULLを無視するためです。条件ごとに集計を1つずつ書けば複数の合計を横に並べられ、これがSQLでピボットテーブルを作る方法です。
欠損値を扱う
優先順にカラムのNULLを確認し、すべて空ならラベルを返します。単純な代替値ならCOALESCEの方が短く書けます。欠損の種類ごとに異なる結果が必要なときはCASEが適しています。
独自の順序で並べ替える
服のサイズ、チケットの優先度、おすすめ商品を先頭にしたリストなど、アルファベット順や数値順では合わないものもあります。ORDER BYでCASEを使えば、各値を好きな位置に配置できます。
CASEの実例
部門と価格で商品を分類する
結果自体をCASEにできるので、部門ごとに独自の価格帯を設定できます。
-- Input: products
-- product_name | category | price
-- -----------------+-------------+--------
-- Laptop Pro 14 | Electronics | 1499.00
-- Wireless Earbuds | Electronics | 129.00
-- Leather Jacket | Clothing | 349.00
-- Cotton T-Shirt | Clothing | 19.99
-- Coffee Mug | Kitchen | 12.50
SELECT
product_name,
CASE category
WHEN 'Electronics' THEN
CASE
WHEN price > 1000 THEN 'Premium Electronics'
ELSE 'Standard Electronics'
END
WHEN 'Clothing' THEN
CASE
WHEN price > 200 THEN 'Designer Clothing'
ELSE 'Regular Clothing'
END
ELSE 'Other Products'
END AS product_class
FROM products;
-- Output:
-- product_name | product_class
-- -----------------+---------------------
-- Laptop Pro 14 | Premium Electronics
-- Wireless Earbuds | Standard Electronics
-- Leather Jacket | Designer Clothing
-- Cotton T-Shirt | Regular Clothing
-- Coffee Mug | Other Products
2階層を超えると、ネストしたCASEは読みにくくなります。参照テーブルを作ってJOINする方が通常はわかりやすいです。
顧客ランク別に割引を適用する
各ランクコードを割引率に対応付け、ランクのない顧客は割引なしとします。
-- Input: customers
-- id | customer_name | tier
-- ---+---------------+-------
-- 1 | Maya Brooks | gold
-- 2 | Leo Martin | silver
-- 3 | Ava Kim | bronze
-- 4 | Noah Price | NULL
-- Input: orders
-- id | customer_id | order_total
-- ----+-------------+------------
-- 101 | 1 | 200.00
-- 102 | 2 | 150.00
-- 103 | 3 | 80.00
-- 104 | 4 | 60.00
SELECT
customer_name,
order_total,
ROUND(order_total * CASE tier
WHEN 'gold' THEN 0.15
WHEN 'silver' THEN 0.10
WHEN 'bronze' THEN 0.05
ELSE 0
END, 2) AS discount
FROM customers c
JOIN orders o ON c.id = o.customer_id
ORDER BY o.id;
-- Output:
-- customer_name | order_total | discount
-- --------------+-------------+---------
-- Maya Brooks | 200.00 | 30.00
-- Leo Martin | 150.00 | 15.00
-- Ava Kim | 80.00 | 4.00
-- Noah Price | 60.00 | 0.00
月別売上をカラムにピボットする
月ごとにSUM(CASE ...)を1つずつ書くと、売上の行が月別のカラムに変わります。
-- Input: sales
-- product_category | sale_date | amount
-- -----------------+------------+-------
-- Books | 2026-01-12 | 40.00
-- Games | 2026-01-20 | 60.00
-- Books | 2026-02-03 | 25.00
-- Books | 2026-03-15 | 30.00
-- Games | 2026-03-28 | 75.00
SELECT
product_category,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 1 THEN amount ELSE 0 END) AS jan_sales,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 2 THEN amount ELSE 0 END) AS feb_sales,
SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 3 THEN amount ELSE 0 END) AS mar_sales
FROM sales
GROUP BY product_category
ORDER BY product_category;
-- Output:
-- product_category | jan_sales | feb_sales | mar_sales
-- -----------------+-----------+-----------+----------
-- Books | 40.00 | 25.00 | 30.00
-- Games | 60.00 | 0 | 75.00
SQL ServerではEXTRACTの代わりにMONTH(sale_date) = 1を、SQLiteではstrftime('%m', sale_date) = '01'を使います。
欠損値と空文字を区別する
インポートしたデータにはNULLと空文字の両方が含まれていることがよくあります。CASEでそれぞれにラベルを付ければ、何をクリーンアップすべきかがわかります。
-- Input: users
-- id | middle_name
-- ---+------------
-- 1 | Rose
-- 2 |
-- 3 | NULL
SELECT
id,
CASE
WHEN middle_name IS NULL THEN 'Missing'
WHEN middle_name = '' THEN 'Empty'
ELSE middle_name
END AS middle_name
FROM users;
-- Output:
-- id | middle_name
-- ---+------------
-- 1 | Rose
-- 2 | Empty
-- 3 | Missing
おすすめ商品を先頭に表示する
各カテゴリに並び順を割り当て、その中で名前順に並べ替えます。
-- Input: products
-- product_name | category
-- -------------+------------
-- Wool Scarf | Sale
-- Desk Lamp | Featured
-- Bike Helmet | Clearance
-- Smart Watch | New Arrival
-- Air Fryer | Featured
SELECT product_name, category
FROM products
ORDER BY
CASE category
WHEN 'Featured' THEN 1
WHEN 'New Arrival' THEN 2
WHEN 'Sale' THEN 3
ELSE 4
END,
product_name;
-- Output:
-- product_name | category
-- -------------+------------
-- Air Fryer | Featured
-- Desk Lamp | Featured
-- Smart Watch | New Arrival
-- Wool Scarf | Sale
-- Bike Helmet | Clearance
CASEについてさらに詳しく
このブログの記事:
- SQL COALESCEガイド: 値がNULLのときに代替値を返す簡単な方法
- SQLピボットテーブルガイド: SUMやAVGの中でCASEを使って行をカラムに変換
- SQL ORDER BYガイド: 独自の並び順を含む並べ替え
- SQL GROUP BYガイド: 条件付きのカウントや合計の土台となるグループ化
- SQL不等号ガイド: NULLとの比較が決して一致しない理由
公式ドキュメント:
Beekeeper Studioは無料のオープンソースのデータベースGUIです
これまで使った中で最高のSQLクエリ&エディタツールです。データベースの管理に必要なものがすべて揃っています。 - ⭐⭐⭐⭐⭐ Mit
Beekeeper Studioは高速で直感的、そして使いやすいツールです。多数のデータベースに対応し、Windows、Mac、Linuxで快適に動作します。
Beekeeper Studio 利用者の声
「Beekeeper Studioは以前のSQLワークフローを完全に置き換えました。高速で直感的で、データベース作業が再び楽しくなりました。」
「多くのデータベースGUIを試しましたが、Beekeeperは機能とシンプルさのバランスが完璧です。とにかくちゃんと動きます。」