group by多字段顺序决定层级结构:左字段粒度粗、右字段粒度细,如group by region, city, store形成“大区→城市→门店”三级汇总;顺序颠倒则破坏逻辑层级,且rollup生成的null表示汇总行而非数据缺失。

GROUP BY 多字段顺序决定层级结构
SQL 里没有“多级分类”的语法糖,GROUP BY 的层级感完全来自字段排列顺序:越靠左的字段粒度越粗,越靠右的越细。比如 GROUP BY region, city, store,天然形成“大区 → 城市 → 门店”三级汇总结构,SUM(sales) 的结果就按这个嵌套关系聚合。
常见错误是把字段顺序搞反,比如写成 GROUP BY store, city, region,结果看似有三列,但逻辑上变成“先按门店分组”,后续 city 和 region 就失去上级意义——因为每个 store 只属于一个 city,分组后 city 列实际只剩单值,无法体现城市级汇总。
- 想看“每个城市的总销量”,必须把
city放在GROUP BY最左侧(或单独出现) - 若同时要城市总量 + 城市下各门店明细,得用
GROUP BY city, store,不能只写GROUP BY city或GROUP BY store - 字段顺序一旦固定,
SELECT中非聚合列必须严格匹配该顺序(否则 MySQL 5.7+ 会报错,PostgreSQL 一直严格)
ROLLUP 能自动生成小计行,但要注意 NULL 含义
GROUP BY region, city, store WITH ROLLUP 会在结果末尾自动补上 region 小计、全量总计等行,看起来像带折叠的树形结构。但这些补出来的行里,缺失层级的字段值是 NULL,不是空字符串或默认值。
比如某行 region = '华东'、city = NULL、store = NULL,表示这是“华东大区所有城市+门店的合计”,不是数据缺失。容易误判为脏数据而过滤掉——千万别加 WHERE city IS NOT NULL,否则直接砍掉所有小计行。
-
WITH ROLLUP只支持 MySQL;PostgreSQL 需用GROUPING SETS替代 - MySQL 8.0+ 支持
GROUPING()函数区分真实NULL和汇总产生的NULL,例如GROUPING(city) = 1表示这行是城市级小计 - ROLLUP 生成的行数 = 字段数 + 1,三个字段最多产生 4 层汇总(含总计),别指望它输出中间任意组合
用子查询或 CTE 分层计算时,GROUP BY 必须和外层对齐
如果要分别算“城市销量占比”和“门店销量占比”,不能在一个 GROUP BY 里硬凑。更稳妥的做法是用 CTE 先算出城市级汇总,再和原始表 JOIN 计算比例:
WITH city_total AS ( SELECT city, SUM(sales) AS city_sum FROM sales_data GROUP BY city ) SELECT s.city, s.store, s.sales, ROUND(s.sales * 100.0 / ct.city_sum, 2) AS pct_in_city FROM sales_data s JOIN city_total ct ON s.city = ct.city;
这里关键点是:CTE 里的 GROUP BY city 和主查询中用于 JOIN 的 s.city 必须语义一致;如果原始表里 city 有大小写混用或空格,CTE 分组结果可能漏匹配,导致 pct_in_city 为 NULL。
- 避免在子查询里用
SELECT *+GROUP BY,容易触发 only_full_group_by 报错 - CTE 不是视图,每次执行都会重算,大数据量时注意性能,必要时可建物化临时表
- 如果层级超过 3 级(如 region→city→store→product),优先考虑预聚合表,而非嵌套 CTE
ORDER BY 和 GROUP BY 的字段不一定要相同,但排序逻辑要自洽
GROUP BY 定义聚合维度,ORDER BY 控制输出顺序,两者独立。但为了阅读体验接近“树形展开”,通常让 ORDER BY 字段前缀和 GROUP BY 一致,比如 GROUP BY region, city, store 配 ORDER BY region, city, store。
如果写成 ORDER BY store, region,结果会打乱层级视觉——同一城市的门店可能被不同大区的记录隔开,人工核对极易出错。更隐蔽的问题是:某些 BI 工具(如 Tableau)依赖 SQL 返回的物理顺序做折叠渲染,顺序错乱会导致前端层级显示异常。
- MySQL 5.7 默认关闭
sql_mode=ONLY_FULL_GROUP_BY,允许SELECT中出现未在GROUP BY列表里的非聚合字段,但此时ORDER BY若引用这些字段,结果不可预测 - PostgreSQL 强制要求
ORDER BY字段必须出现在SELECT列表或GROUP BY中,提前暴露逻辑矛盾 - 层级汇总结果常导出到 Excel,建议在 SQL 末尾加
ORDER BY,别依赖客户端排序
实际写多级汇总时,最常被忽略的是字段值的标准化程度——比如 city 字段里混着 “上海”、“shanghai”、“SH” 三种写法,GROUP BY city 会当成三个不同组。这类问题不会报错,但汇总数字永远对不上业务口径。











