必须将case when嵌套在sum、count等聚合函数内,否则group by会因非聚合列报错;动态宽表需显式枚举各分支并统一写else 0,且group by中的case表达式须与select中字面完全一致。

必须把 CASE WHEN 套进 SUM、COUNT 等聚合函数里,否则直接报错或结果错乱。
GROUP BY + CASE WHEN 报错:1055 或 “non-aggregated column”
这是最常见卡点:写了 CASE WHEN status = 'paid' THEN amount END,但没包聚合函数,又加了 GROUP BY order_date。MySQL 会直接拒绝执行,PostgreSQL 可能报错“column must appear in the GROUP BY clause”。
- 根本原因:SQL 执行顺序是先分组、再聚合。
CASE WHEN是行级表达式,不属于分组字段,也不属于聚合结果,数据库无法确定每组该返回哪一行的值 - 别指望用别名绕过,
SELECT CASE WHEN ... END AS paid_amt后在GROUP BY paid_amt是无效的——GROUP BY只认字面表达式,不认别名 - 正确姿势:所有条件逻辑必须包裹在聚合函数内,例如
SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END)
SUM(CASE WHEN) vs COUNT(CASE WHEN):语义和 NULL 处理差异
两者都能做条件计数,但行为不同,容易算错:
-
COUNT(CASE WHEN score >= 90 THEN 1 END):只统计满足条件的行数(NULL不计入),等价于SUM(CASE WHEN score >= 90 THEN 1 ELSE 0 END),但前者语义更贴近“人数” -
SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END):适合带数值的条件求和;若漏写ELSE 0,不匹配的行变成NULL,SUM会跳过它们,导致总和偏小 - 别写
CASE WHEN ... THEN SUM(amount) END——这会让每组输出多个值,违反聚合语义,直接报错
动态列(宽表)必须显式枚举,且每个分支都要写 ELSE 0
想把“订单类型-金额”长表转成 type_A_amt、type_B_amt 这样的宽表,不能靠数据库自动推导列名:
- 每个目标列对应一个
SUM(CASE WHEN type = 'A' THEN amount ELSE 0 END) AS type_A_amt,所有列共享同一个GROUP BY date - 必须写
ELSE 0,而不是留空或ELSE NULL:否则某天没有 type='A' 的记录时,该列返回NULL,后续计算(比如除法、比较)容易出错 - 注意类型一致性:
amount是 DECIMAL,就别让ELSE 'N/A'混进来,会触发隐式转换或报错 - 枚举值要全:如果实际有 type='C' 但没写对应分支,这部分数据就彻底丢弃,不会警告
GROUP BY 中的 CASE WHEN 表达式必须和 SELECT 中完全一致
哪怕多一个空格、括号位置不同、大小写不一致,MySQL 就当成两个不同表达式,导致分组失效或报错:
- ✅ 正确:
SELECT CASE WHEN age >= 18 THEN 'adult' ELSE 'minor' END, COUNT(*) FROM users GROUP BY CASE WHEN age >= 18 THEN 'adult' ELSE 'minor' END - ❌ 错误:
SELECT CASE WHEN age >= 18 THEN 'adult' ELSE 'minor' END AS age_group FROM users GROUP BY age_group(别名无效) - ❌ 错误:
GROUP BY CASE WHEN age>=18 THEN 'adult' ELSE 'minor' END(少空格,和 SELECT 中不一致)
真正难调试的点就在这儿:语义相同、字面不同,SQL 就不认。建议复制粘贴,别手敲两遍。










