CASE WHEN 在 GROUP BY 中必须与聚合函数配合使用,否则会因非聚合字段与聚合结果混用而报错;正确写法是将 CASE WHEN 表达式同时用于 SELECT 和 GROUP BY,或用 SUM(CASE WHEN...) 等外层聚合实现单次扫描多条件统计。

CASE WHEN 在 GROUP BY 中必须和聚合函数配合使用
直接在 SELECT 里写 CASE WHEN 却忘了加 SUM()、COUNT() 这类聚合函数,是初学者最常踩的坑。SQL 不允许非聚合字段和聚合结果混在同一 SELECT 中(除非该字段在 GROUP BY 里)。比如想统计“高薪/中薪/低薪”人数,不能只写:
SELECT CASE WHEN salary > 15000 THEN 'high' ... END FROM emp;
而必须包一层聚合:
SELECT
CASE WHEN salary > 15000 THEN 'high'
WHEN salary BETWEEN 8000 AND 15000 THEN 'mid'
ELSE 'low' END AS level,
COUNT(*) AS cnt
FROM emp
GROUP BY CASE WHEN salary > 15000 THEN 'high'
WHEN salary BETWEEN 8000 AND 15000 THEN 'mid'
ELSE 'low' END;
- GROUP BY 后面的表达式必须和 SELECT 中的
CASE WHEN完全一致(部分数据库如 MySQL 允许用别名,但 PostgreSQL、SQL Server 不行) - 如果只按分类统计总数,不涉及分组维度,可省略 GROUP BY,改用聚合函数包裹整个
CASE WHEN,例如:SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) -
CASE WHEN本身不聚合,它只是生成中间值;真正做“统计”的是外层的COUNT、SUM等
用 SUM(CASE WHEN ...) 替代多个 COUNT + WHERE 组合
需要同时统计多个互斥条件(如:男数、女数、未知数)时,写三个独立子查询或 UNION 很冗余。更高效的做法是单次扫描 + 多个 SUM(CASE WHEN ...):
SELECT
SUM(CASE WHEN gender = 'M' THEN 1 ELSE 0 END) AS male_cnt,
SUM(CASE WHEN gender = 'F' THEN 1 ELSE 0 END) AS female_cnt,
SUM(CASE WHEN gender NOT IN ('M','F') THEN 1 ELSE 0 END) AS other_cnt
FROM users;
- 所有条件共用同一张表的一次扫描,IO 和执行效率明显优于多次
COUNT(*) WHERE ... - 务必用
SUM而不是COUNT:因为COUNT(CASE WHEN ...)会忽略 ELSE 的 NULL,导致“不满足条件”的行被漏计;而SUM(... THEN 1 ELSE 0)显式覆盖所有分支 - 注意 NULL 处理:如果原始字段可能为 NULL(如
gender IS NULL),需单独判断,否则会被归入 ELSE 分支
避免 CASE WHEN 条件重叠或遗漏导致统计偏差
条件顺序决定匹配优先级——SQL 按从上到下第一个为 TRUE 的分支返回结果。常见错误是区间定义交叉或留空隙:
-- ❌ 错误:12000 同时满足两个条件,实际只会进入第一个分支
CASE WHEN salary >= 10000 THEN 'A'
WHEN salary >= 12000 THEN 'B' -- 永远不会执行
<p>-- ❌ 错误:salary = 9500 不属于任何分支,结果为 NULL
CASE WHEN salary >= 10000 THEN 'high'
WHEN salary </p>
- 区间建议用
BETWEEN或明确的上下界(如>= 5000 AND ),避免边界模糊 - 始终以
ELSE收尾,哪怕只是ELSE 'unknown',防止意外 NULL 影响后续分析 - 上线前用
SELECT DISTINCT salary抽样检查边界值,确认每个典型数值都落入预期分支
在窗口函数中嵌套 CASE WHEN 实现动态分组累计
想按分类做累加(比如“女性用户每日注册数的滚动求和”),不能只靠 GROUP BY,得结合窗口函数:
SELECT
reg_date,
SUM(CASE WHEN gender = 'F' THEN 1 ELSE 0 END)
OVER (ORDER BY reg_date ROWS UNBOUNDED PRECEDING) AS female_cumsum
FROM users
GROUP BY reg_date;
- 这里先用
GROUP BY reg_date汇总每日数据,再用SUM(...) OVER (...)做时间维度累加 - 窗口函数里的
CASE WHEN和普通聚合一样,必须返回数值(通常 0/1),不能返回字符串 - 注意
OVER子句中的排序字段(如reg_date)必须确定、无重复,否则窗口行为不可控;如有重复日期,需加二级排序(如ORDER BY reg_date, id)
真实场景里,最难的往往不是语法,而是把业务规则准确映射成无重叠、无遗漏、可复用的 CASE 分支。尤其当分类逻辑来自产品文档或历史 Excel 表格时,先人工核对 5–10 个典型样本的归属,比调通 SQL 更关键。










