GROUP BY a, b 是按复合键分组而非嵌套分组,顺序决定语义(如 region, city 与 city, region 结果不同),需配合 WITH ROLLUP 或 GROUPING SETS 实现小计总计,并用 GROUPING() 区分真实 NULL 与汇总占位符。

直接写 GROUP BY field1, field2 就是双重分组,不是“嵌套语法”,而是按字段顺序逐层切分数据——先按 field1 拆大块,每块内再按 field2 细分。
为什么 GROUP BY a, b 不等于两个独立 GROUP BY 套用?
SQL 不允许在同一个查询里写两层 GROUP BY。所谓“双重分组”,本质是把 (a, b) 当作一个复合键来分组,不是先按 a 聚一次、再拿结果按 b 聚一次。常见误解是想复刻 Excel 数据透视表的“行标签+列标签”结构,但 SQL 返回的永远是扁平表,没有行列嵌套格式。
容易踩的坑:
- 漏写非聚合字段:比如
SELECT region, city, COUNT(*)却只写GROUP BY region,MySQL 5.7+ 会报错Expression #2 of SELECT list is not in GROUP BY clause - 误以为字段顺序无关:写成
GROUP BY city, region会生成“城市→地区”的分组逻辑,和GROUP BY region, city的语义完全不同 - 依赖隐式排序:不加
ORDER BY时,部分数据库按GROUP BY字段顺序输出,但这不是标准行为,不能用于生产逻辑
怎么同时看到小计和总计?别硬套子查询
如果目标是“每个城市销量 + 每个地区的城市小计 + 全部总计”,靠 GROUP BY region, city 是不够的——它只返回有数据的 (region, city) 组合。必须用汇总扩展语法:
- MySQL 8.0+:用
GROUP BY region, city WITH ROLLUP,会自动补出(region, NULL)(城市小计)和(NULL, NULL)(总计)行 - PostgreSQL / SQL Server / Oracle:用
GROUP BY GROUPING SETS ((region), (region, city)),更灵活,支持任意粒度组合 - 所有情况都得配合
GROUPING()判断 NULL 是真实值还是汇总占位符,例如CASE WHEN GROUPING(city) = 1 THEN '小计' ELSE city END
别在 SELECT 里直接 IFNULL(city, '小计') ——万一原始数据真有 city = '小计',就混淆了。
日期类双重分组最容易出错的地方
按年+季度、或年+月分组时,函数必须在 GROUP BY 和 SELECT 中**完全重复写表达式**,不能用别名:
SELECT YEAR(order_date) AS year, QUARTER(order_date) AS quarter, COUNT(*) FROM orders GROUP BY YEAR(order_date), QUARTER(order_date); -- ✅ 正确
错误写法:GROUP BY year, quarter(多数数据库不认)。
还要注意时间范围控制:
- 只写
WHERE order_date >= '2023-01-01'不够,得加上AND order_date ,否则跨年数据混入 - 避免用
MONTH(order_date) BETWEEN 10 AND 12单独筛 Q4——它会拉所有年份的 10–12 月,丢失年份上下文
真正难的不是写法,而是意识到:SQL 的 GROUP BY 天然不生成树形结构。你要的“嵌套展示效果”,得靠应用层拼装,或者用 GROUPING SETS + GROUPING() 手动标注层级——这点很容易被忽略,直到报表导出时发现小计行对不上。










