动态列汇总必须用case when+聚合函数实现,sql本身不支持自动识别枚举值并生成列名;需手写各分类的case分支并显式写else 0,配合group by完成行转列。

动态列汇总必须用 CASE WHEN + 聚合,不能靠 GROUP BY 自动展开
SQL 没有“自动识别唯一值并生成对应列”的语法,所谓“动态分类汇总”,本质是把离散的类别值(如 state、category)硬编码为多组 CASE WHEN 表达式,再套上 SUM() 或 COUNT()。数据库不会帮你扫描表里有多少个 state 值然后生成对应列——那是应用层或动态 SQL 的事。
常见错误是试图写:SELECT cst_id, state, SUM(quantity) FROM ordEdit GROUP BY cst_id, state,这只能得到长格式结果(每行一个 cst_id+state),不是 Excel 那种宽表结构(cst_id、M、R、C 同行显示)。
- 固定类别场景:直接手写
CASE WHEN state = 'M' THEN quantity END,配合SUM()聚合 - 类别数量未知或常变:必须用动态 SQL 拼接语句(如 SQL Server 的
EXEC(@sql)或 MySQL 的PREPARE),否则无法生成列名 - 注意
ELSE 0:漏写会导致NULL,影响求和结果;显式写ELSE 0更安全
动态 SQL 拼接时,GROUP BY 和 SELECT 必须严格对齐
拼出来的 SQL 字符串里,GROUP BY 子句不能只写 cst_id 就完事——如果 SELECT 中有聚合字段以外的列(比如加了 MAX(created_at)),它们也得出现在 GROUP BY 中,否则在严格模式下直接报错 ERROR 1055。
示例中只对 cst_id 分组,是因为所有输出列都是聚合结果(SUM(CASE...))。一旦你要加一个非聚合字段如 region,就必须同步加进 GROUP BY region,且该字段也要参与动态拼接逻辑,否则语句会崩。
- 拼接前先查出所有唯一值:
SELECT DISTINCT state FROM ordEdit - 每轮循环拼一个
SUM(CASE WHEN state = 'X' THEN quantity ELSE 0 END) AS X - 最终字符串必须包含完整
SELECT、FROM、GROUP BY,缺一不可 - MySQL 8.0+ 不支持直接执行字符串,得用
PREPARE+EXECUTE两步走
GROUPING() 是识别 ROLLUP 汇总行的唯一方式,IS NULL 不可靠
当你用 GROUP BY dept, role WITH ROLLUP 生成小计行时,数据库会在 role 或 dept 列填 NULL。但原始数据里也可能真有 role IS NULL 的记录——仅靠 IS NULL 判断,根本分不清哪行是汇总、哪行是脏数据。
GROUPING(dept) 返回 1 表示这行的 dept 值是 ROLLUP 自动生成的占位符,返回 0 才是真实数据。这个函数必须作用于 GROUP BY 中参与 ROLLUP 的列,否则报错。
- MySQL 8.0+ 支持
GROUPING(),但旧版不支持,只能用COALESCE(dept, '总计')+ 业务规则推断,风险高 - PostgreSQL 和 SQL Server 对
GROUPING()支持更稳,推荐优先使用 - 排序时要把
GROUPING()结果纳入ORDER BY,否则汇总行可能被挤到明细中间
ROLLUP 的层级顺序决定小计路径,写反了就丢维度
GROUP BY a, b, c WITH ROLLUP 产生的汇总路径是单向树状:从最细粒度 (a,b,c) → 上一级 (a,b,NULL) → 再上一级 (a,NULL,NULL) → 全局 (NULL,NULL,NULL)。它不会生成 (NULL,b,c) 或 (a,NULL,c) 这类跨级组合。
所以如果你要“部门 → 小组 → 姓名”三级汇总,GROUP BY 的列顺序必须是 dept, team, name;写成 name, team, dept 就只剩 name 级明细 + 两层空值,小组、部门的小计全没了。
- 业务层级由粗到细排列:先大类(如
region),再中类(city),最后小类(store) - 想跳过中间层?ROLLUP 不行,得换
GROUPING SETS((region), (city), ()) - MySQL 对 ROLLUP 输出中
NULL的排序行为不稳定,务必用GROUPING()控制顺序
GROUPING() 判定)、哪些必须推给应用层(如动态列名渲染),以及哪些看似能绕过去的地方(比如用 IS NULL 代替 GROUPING())会在数据稍有变化时突然失效。











