sum(case when ...) 是最稳、最通用的行转列写法,兼容 mysql 5.7 至 8.0,基于「分组 + 条件聚合」实现,需 group by userid,用 sum 更安全,else 0 比 null 更稳妥,字段名须加反引号。

SUM(CASE WHEN ...) 是最稳、最通用的行转列写法,MySQL 5.7 到 8.0 都能直接跑通,不用等 PIVOT。
用 SUM(CASE WHEN subject = '语文' THEN score ELSE 0 END) 最省心
这是兼容性最强、执行最可控的方式。核心是「分组 + 条件聚合」,不是真把表结构改了,只是查询时动态拼列。
-
GROUP BY userid必须加,否则所有用户成绩会叠到一行 - 用
SUM()而不是MAX()更安全:即使某人同一科目有多条记录(比如补考),SUM能累加,MAX只取最高分——得看业务要不要去重 -
ELSE 0比ELSE NULL更稳妥:NULL在SUM中会被忽略,但列名显示可能为空白;填0一眼看出“没这科成绩” - 字段名带中文或特殊字符,必须用反引号包裹,比如
`subject`,否则报错Unknown column 'subject' in 'field list'
PIVOT 在 MySQL 8.0+ 真有,但别急着用
MySQL 8.0.29+ 确实加了 PIVOT 语法,但实际限制多,远不如手写 CASE 灵活。
- 不支持动态列名——你得提前写死所有
IN ('语文', '数学', '英语'),和CASE一样 - 要求子查询必须有别名,且外层不能直接
SELECT *,容易卡在Every derived table must have its own alias - 聚合函数只认
SUM/AVG/COUNT等,不支持自定义逻辑(比如“取最新一条成绩”就得绕开) - 错误信息不友好,比如漏写
AS pivot_table,报错是You have an error in your SQL syntax,看不出哪缺了
遇到科目数量不确定?别硬写死,用 GROUP_CONCAT + 应用层拼接
纯 SQL 动态生成列(比如自动发现新科目并加一列)在 MySQL 里没有安全又简洁的办法。强行用 PREPARE + EXECUTE 写动态 SQL,容易被注入、难调试、无法缓存执行计划。
- 推荐做法:先查出所有科目
SELECT GROUP_CONCAT(DISTINCT subject) FROM tb_score,拿到'语文,数学,英语,政治' - 在应用代码(Python/Java/PHP)里拆成数组,拼出完整的
CASE查询语句再执行 - 如果只是展示用,
GROUP_CONCAT(score ORDER BY subject SEPARATOR ' | ')把成绩合并成字符串也够用,避免大宽表 - 千万别在生产环境用存储过程拼 SQL 字符串后
EXECUTE——权限、日志、监控全断层
性能和 NULL 值处理最容易踩坑
行转列本身不慢,但几个细节会让结果错或变慢:
- 没加
WHERE subject IN ('语文','数学','英语')过滤,会导致CASE分支多判无效值,IO 和 CPU 白耗 - 字段类型不一致:比如
score是VARCHAR存数字,SUM()会静默转成 0,查不出问题但结果为 0 - 索引没建对:至少要有
(userid, subject)联合索引,否则GROUP BY全表扫描 -
IFNULL(MAX(...), 0)和SUM(...) ELSE 0行为不同:前者先聚合再补零,后者每行都判断,大数据量下后者略快
CASE 控制力最强,也最经得起线上压测。











