CASE WHEN,90% 的人只用了第一种写法
你写了很多 SQL,用
CASE WHEN的方式,大概只有两种: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 的写法,收成一次扫描加分组。
你做用户地域分析,城市有 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;"其他城市"永远排最后。
这是 CASE WHEN 在分析场景里最有价值的用法,没有之一。
看场景:一张订单表,每行是一个用户一个月的消费记录。老板要看每个用户 1 月、2 月、3 月分别花了多少钱,列对齐——这是行转列。
一条 SQL:
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;结果:
原理:每一列的 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;这是分析师用得最多、但教程写得最少的一个用法。
场景:同时看"付费用户数"、"免费用户数"、"付费金额"——四个指标,正常写法是四个子查询。
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;注意:CASE WHEN 写在 COUNT(DISTINCT ...) 里面,相当于先过滤再计数。COUNT(col) 忽略 NULL,所以 COUNT 场景下 ELSE NULL 跟不写 ELSE 效果一样。
但 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;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;条件顺序很重要。 上面把 order_cnt >= 10 的判断放在前面先匹配,如果反过来写,低频高价值用户会被优先匹配为"低频高价值",而实际上如果他同时满足 order_cnt >= 3 应该归入"中频高价值"。
把前面所有用法串在一起,做一张真实业务里常用的报表:
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,城市归类、行转列透视、季度环比增长率,全部搞定。
-- 基础语法
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
阅读原文:原文链接