order by 必须结合 grouping() 函数排序才能保持 rollup 层级结构,直接按字段值排序会因 null 占位符错位导致小计行混入明细中;group by 字段顺序须与业务层级一致,且 order by 中 grouping() 列顺序须严格对应。

ORDER BY 直接按字段值排序会打乱 ROLLUP 的层级结构
ROLLUP 生成的汇总行(如 city IS NULL、region IS NULL)依赖 NULL 占位符在结果集中所处的逻辑位置来表达“小计”或“总计”。但 ORDER BY city, region 这类写法会让所有 city IS NULL 的行被排到最前面(默认升序),导致部门小计出现在明细之前,甚至夹在不同 region 之间——人眼无法识别哪行是“华东小计”,哪行是“真实城市为 NULL 的数据”。
正确排序必须区分“汇总行”和“明细行”
靠字段值本身排序不可靠,必须用能反映分组层级的标识:
-
GROUPING(region)返回 1 表示该行的region是 ROLLUP 生成的占位 NULL,不是原始数据缺失 - MySQL 8.0+ 可直接用:
ORDER BY GROUPING(region), region, GROUPING(city), city - MySQL 5.7 或 SQL Server 中可用:
ORDER BY CASE WHEN region IS NULL THEN 1 ELSE 0 END, region模拟分组标识 - PostgreSQL 同样支持
GROUPING(),且必须配合GROUPING SETS使用
字段顺序错位会导致 GROUPING() 判定失效
如果写成 GROUP BY city, region WITH ROLLUP,那 GROUPING(city) = 1 的行实际表示“所有城市的汇总”,即按 region 小计——但业务上你本意是先按 region 再按 city。此时 GROUPING() 值依然准确,但语义已和业务层级脱钩。所以:
-
GROUP BY字段顺序必须和业务层级一致(例如:region → team → member) -
ORDER BY中的GROUPING()列顺序也要严格对应,不能调换 - 别指望靠
ORDER BY把“总计行”固定在最后一行——它只是按逻辑层级排序,不是物理锚点
PostgreSQL 没有 WITH ROLLUP,但 GROUPING SETS 排序逻辑一样危险
PostgreSQL 不支持 GROUP BY ... WITH ROLLUP,必须写成 GROUP BY GROUPING SETS ((region, city), (region), ())。看起来只是语法差异,但陷阱相同:
- 如果写成
GROUP BY GROUPING SETS ((city, region), (city), ()),小计逻辑就反了 -
ORDER BY仍需用GROUPING(region), GROUPING(city)控制层级顺序 - 漏掉
GROUPING()而只写ORDER BY region, city,汇总行照样乱序











