rollup正确写法需按字段顺序逐级上卷,mysql用with rollup,postgresql/sql server用group by rollup(...);须用grouping()函数或coalesce()显式区分汇总行与真实null值,避免误判。

ROLLUP 生成分组合计的正确写法
直接用 GROUP BY 加 WITH ROLLUP(MySQL)或 GROUP BY ROLLUP(...)(PostgreSQL/SQL Server)就能同时输出明细和各级合计,但结果顺序和空值含义容易误读。
关键不是“能不能”,而是“怎么让合计行出现在预期位置、且不和真实数据混淆”。MySQL 的 WITH ROLLUP 会把合计行插在每组末尾,用 NULL 标识汇总层级;而标准 SQL 的 ROLLUP(a,b) 生成 (a,b)、(a,NULL)、(NULL,NULL) 三类行——注意:不是 (a,b) + (a) + 总计,而是按括号内字段从右到左逐级上卷。
-
ROLLUP(country, city)产生:每个 (country, city) 组 → 每个 country 小计 → 全局总计 -
ROLLUP(city, country)则先按 city 上卷,逻辑完全不同,慎调顺序 - MySQL 不支持标准
ROLLUP()语法,必须用WITH ROLLUP,且只允许跟在GROUP BY后
如何区分明细行和合计行
不能靠字段是否为 NULL 简单判断——因为原始数据本身可能含 NULL。得用 GROUPING() 函数(标准 SQL)或 ISNULL() + 逻辑推断(MySQL)。
PostgreSQL/SQL Server 推荐写法:
SELECT COALESCE(country, '总计') AS country, COALESCE(city, '小计') AS city, SUM(sales) AS total, GROUPING(country) AS g_country, GROUPING(city) AS g_city FROM orders GROUP BY ROLLUP(country, city);
GROUPING(country) 返回 1 表示该行是 country 级汇总(即 city 有值但 country 为 NULL),返回 0 表示真实数据。比手动判 country IS NULL 更可靠。
- MySQL 没有
GROUPING(),只能用IF(city IS NULL AND country IS NOT NULL, 'country_total', ...)做多层判断 - 若原始表 city 允许 NULL,则
city IS NULL无法区分是真实 NULL 还是汇总占位符 - 建议在 SELECT 中用
COALESCE()或IF()显式标注,避免下游解析出错
ROLLUP 和 CUBE、GROUPING SETS 的实际取舍
要的是“逐级累加合计”(如省→市→总计),就用 ROLLUP;如果需要所有组合(省、市、省+市、总计),才考虑 CUBE ——它生成的行数是 2ⁿ,容易爆炸。
-
ROLLUP(a,b,c)生成 4 种分组:(a,b,c)、(a,b)、(a)、(),共 n+1 种 -
CUBE(a,b,c)生成 8 种:所有子集组合,包括 (b,c)、(a,c) 等非前缀组合 - 多数报表场景只需要前缀式汇总,
ROLLUP更可控,性能也更好 - SQL Server 支持
GROUPING SETS写法更灵活,但可读性下降,除非真需要混合模式
ORDER BY 对 ROLLUP 结果的影响
ORDER BY 会重排 ROLLUP 生成的行,可能把合计行拆散。MySQL 的 WITH ROLLUP 要求 ORDER BY 必须匹配 GROUP BY 字段顺序,否则报错;PostgreSQL 允许任意排序,但合计行位置就不可控了。
- 想保持“明细在上、小计紧随其后、总计在最底”,就别加
ORDER BY,依赖数据库默认输出顺序 - 若必须排序,用
GROUPING()辅助:例如ORDER BY GROUPING(country), country, GROUPING(city), city - MySQL 下
ORDER BY country, city是安全的,但ORDER BY sales DESC会让合计行飘到中间,慎用
ROLLUP 的核心陷阱不在语法,而在对 NULL 和分组层级的理解偏差——尤其当字段本身允许 NULL 时,不加 GROUPING() 或等价判断,很容易把汇总行当成脏数据过滤掉。











