case when本身不聚合,仅逐行生成值;要“转成标签”需配合string_agg或group_concat等聚合函数实现字符串拼接。

为什么CASE WHEN不能直接“转成标签”
很多人想用 CASE WHEN 把多行数据(比如用户行为日志)直接“折叠”成一行带多个标签的记录,但 CASE WHEN 本身只是逐行计算表达式,不聚合、不拼接、不去重——它输出的是和输入行数一致的新列值。真要“转成标签”,本质是聚合 + 字符串组合,CASE WHEN 只是其中一环。
正确做法:CASE WHEN + STRING_AGG(或GROUP_CONCAT)
典型场景:一个用户有多条订单记录,想按订单金额是否大于100打上 'high_value' 或 'normal' 标签,并合并为逗号分隔的字符串。
- PostgreSQL / SQL Server 2017+:用
STRING_AGG(CASE WHEN amount > 100 THEN 'high_value' ELSE 'normal' END, ',') - MySQL 8.0+:用
GROUP_CONCAT(CASE WHEN amount > 100 THEN 'high_value' ELSE 'normal' END SEPARATOR ',') - MySQL 5.7 或更早:必须先用子查询或临时表生成标签列,再用
GROUP_CONCAT,否则会报错“Invalid use of group function” - 注意
NULL值:默认STRING_AGG和GROUP_CONCAT会跳过NULL;如果CASE没写ELSE,未匹配行会产生NULL,导致该行无标签
避免常见错误:WHERE 过滤 vs CASE 分类
容易混淆的是把分类逻辑写进 WHERE——比如只查 amount > 100 的订单,再对结果统一打 'high_value' 标签。这会丢失原始分组结构,且无法在同一聚合中并存多个标签。
- ❌ 错误:
WHERE amount > 100后再CASE WHEN TRUE THEN 'high_value' - ✅ 正确:保留所有行,在
CASE内部分支判断,让聚合函数看到全部分类可能性 - 性能提示:如果某标签覆盖 99% 的数据,
CASE判断本身开销极小;但若嵌套多层CASE且字段无索引,WHERE 预过滤仍可提升整体效率
标签去重与排序控制
同一用户可能多次触发相同标签(比如下了 3 单高价订单),但你只需要一个 'high_value'。这时候不能只靠 STRING_AGG,得先去重。
- PostgreSQL:用
STRING_AGG(DISTINCT ...) - MySQL:
GROUP_CONCAT(DISTINCT ...) - 需要固定顺序?加
ORDER BY子句:STRING_AGG(CASE ... END ORDER BY amount DESC, created_at) - 注意:MySQL 的
DISTINCT在GROUP_CONCAT中不支持表达式,所以CASE结果必须是确定性值;若依赖字段别名排序,需在外部查询中处理
DISTINCT 与复杂表达式共用,而 PostgreSQL 对空字符串和 NULL 的聚合行为也和 MySQL 不同。










