CASE WHEN col = 'A' THEN '类目A' WHEN col = 'B' THEN '类目B' ELSE '其他' END
然后就没了。
如果是这样,你可能低估了 CASE WHEN 功力。
01 UNION ALL 替代:三遍扫描 → 一遍扫描
先看一个场景。
业务方要一张报表,按用户等级分层看数据:
-- 等级分布:高中低三个用户群 SELECT '高活跃用户' AS user_group, COUNT(*) AS cnt FROM users WHERE activity_score >= 80 UNION ALL SELECT '中活跃用户' AS user_group, COUNT(*) AS cnt FROM users WHERE activity_score BETWEEN 50 AND 79 UNION ALL SELECT '低活跃用户' AS user_group, COUNT(*) AS cnt FROM users WHERE activity_score < 50;
三个 UNION ALL,三次全表扫描。假设 users 表 1000 万行,前者扫三遍,后者只扫一遍。
用 CASE WHEN,一遍搞定:
SELECT CASE WHEN activity_score >= 80 THEN '高活跃用户' WHEN activity_score >= 50 THEN '中活跃用户' ELSE '低活跃用户' END AS user_group, COUNT(*) AS cnt FROM users GROUP BY 1;
这就是 CASE WHEN 在 GROUP BY 里的威力——把原本 N 个 UNION ALL 的写法,收成一次扫描加分组。
02 把"不重要"的 N 个值归为"其他"
你做用户地域分析,城市有 50 个,但业务上只关心前 5 个,其他统一归为"其他城市":
SELECT CASE WHEN city IN ('北京', '上海', '深圳', '广州', '杭州') THEN city ELSE '其他城市' END AS city_group, COUNT(*) AS user_cnt FROM users GROUP BY city_group ORDER BY user_cnt DESC;
进阶——用 CASE WHEN 控制 ORDER BY 排序顺序:
SELECT CASE WHEN city IN ('北京', '上海', '深圳', '广州', '杭州') THEN city ELSE '其他城市' END AS city_group, COUNT(*) AS user_cnt FROM users GROUP BY city_group ORDER BY CASE WHEN city_group = '其他城市' THEN 1 ELSE 0 END, user_cnt DESC;
SELECT user_id, SUM(CASE WHEN month = '2026-01' THEN spend ELSE 0 END) AS jan_spend, SUM(CASE WHEN month = '2026-02' THEN spend ELSE 0 END) AS feb_spend, SUM(CASE WHEN month = '2026-03' THEN spend ELSE 0 END) AS mar_spend, SUM(spend) AS total_spend FROM monthly_spend WHERE month IN ('2026-01', '2026-02', '2026-03') GROUP BY user_id;
结果:
user_id
jan_spend
feb_spend
mar_spend
total_spend
001
500
800
300
1600
002
200
600
0
800
原理:每一列的 SUM 只累加 CASE WHEN 条件为真的那一行,其他行贡献 0——每列对应一个月,列对齐。
进阶:加一个维度(地区),只需 GROUP BY 多写一个字段:
SELECT region, user_id, SUM(CASE WHEN month = '2026-01' THEN spend ELSE 0 END) AS jan, SUM(CASE WHEN month = '2026-02' THEN spend ELSE 0 END) AS feb, SUM(CASE WHEN month = '2026-03' THEN spend ELSE 0 END) AS mar FROM monthly_spend WHERE month IN ('2026-01', '2026-02', '2026-03') GROUP BY region, user_id ORDER BY region, total_spend DESC;
04 条件聚合:一个 SELECT 同时出多个条件下的指标
这是分析师用得最多、但教程写得最少的一个用法。
场景:同时看"付费用户数"、"免费用户数"、"付费金额"——四个指标,正常写法是四个子查询。
CASE WHEN + 聚合函数,一遍搞定:
SELECT region, -- 付费用户数 COUNT(DISTINCT CASE WHEN is_paid = 1 THEN user_id END) AS paid_user_cnt, -- 免费用户数 COUNT(DISTINCT CASE WHEN is_paid = 0 THEN user_id END) AS free_user_cnt, -- 付费金额 SUM(CASE WHEN is_paid = 1 THEN amount ELSE 0 END) AS paid_amount, -- 免费引导金额 SUM(CASE WHEN is_paid = 0 THEN guide_amount ELSE 0 END) AS free_guide_amount FROM orders GROUP BY region;
但 SUM 必须写 ELSE 0——因为 SUM 遇到 NULL 是不累加的,和 ELSE 0 效果一样,但可读性差,容易漏:
-- ❌ 隐患:SUM 忘了写 ELSE,返回结果看起来对但逻辑不清晰 SELECT SUM(CASE WHEN is_paid = 1 THEN amount END) AS paid_amount FROM orders; -- ✅ 标准写法 SELECT SUM(CASE WHEN is_paid = 1 THEN amount ELSE 0 END) AS paid_amount FROM orders;
05 多条件嵌套分类:WHEN 里还能再套 WHEN
CASE WHEN 可以嵌套,用来处理两个维度交叉的分类。
场景:给用户打标签,消费频次(高/中/低)× 客单价(高/低),组合成 6 类用户:
SELECT user_id, CASE WHEN order_cnt >= 10 AND avg_order_amount >= 500 THEN '高频高价值' WHEN order_cnt >= 10 AND avg_order_amount < 500 THEN '高频低价值' WHEN order_cnt >= 3 AND avg_order_amount >= 500 THEN '中频高价值' WHEN order_cnt >= 3 AND avg_order_amount < 500 THEN '中频低价值' WHEN avg_order_amount >= 500 THEN '低频高价值' ELSE '低频低价值' END AS user_segment FROM user_stats;
WITH city_monthly AS ( SELECT CASE WHEN city IN ('北京', '上海', '深圳', '广州', '杭州') THEN city ELSE '其他城市' END AS city_group, LEFT(order_month, 7) AS month, SUM(order_amount) AS monthly_amount FROM orders WHERE LEFT(order_month, 7) >= '2026-01' AND LEFT(order_month, 7) <= '2026-03' GROUP BY 1, 2 ) SELECT city_group, SUM(CASE WHEN month = '2026-01' THEN monthly_amount ELSE 0 END) AS jan_amount, SUM(CASE WHEN month = '2026-02' THEN monthly_amount ELSE 0 END) AS feb_amount, SUM(CASE WHEN month = '2026-03' THEN monthly_amount ELSE 0 END) AS mar_amount, SUM(monthly_amount) AS q1_total, ROUND( (SUM(CASE WHEN month = '2026-03' THEN monthly_amount ELSE 0 END) - SUM(CASE WHEN month = '2026-01' THEN monthly_amount ELSE 0 END)) * 100.0 / NULLIF(SUM(CASE WHEN month = '2026-01' THEN monthly_amount ELSE 0 END), 0), 2 ) AS q1_growth_pct FROM city_monthly GROUP BY city_group ORDER BY q1_total DESC;
一个 CTE + 一层 SELECT,城市归类、行转列透视、季度环比增长率,全部搞定。
07 CASE WHEN 语法速查
-- 基础语法 CASE WHEN 条件1 THEN 结果1 WHEN 条件2 THEN 结果2 WHEN 条件3 AND 条件4 THEN 结果3 ELSE 默认结果 -- 建议永远写上 END -- 在 GROUP BY 里 GROUP BY CASE WHEN ... END -- 在聚合函数里 SUM(CASE WHEN condition THEN col ELSE 0 END) COUNT(DISTINCT CASE WHEN condition THEN col END) -- 嵌套 CASE WHEN 条件1 THEN CASE WHEN 子条件A THEN 'A1' ELSE 'A2' END WHEN 条件2 THEN 'B' ELSE 'C' END