GROUP BY a, b 将(a,b)视为联合键整体分组,相同a、b值(含NULL相等)归为一组;顺序不影响分组结果但影响默认排序;SELECT非聚合列必须严格出现在GROUP BY中。

GROUP BY 多列分组的写法和语义
直接在 GROUP BY 后面用逗号分隔多个列名,例如 GROUP BY department, gender。这不是“先按 A 分再按 B 分”的嵌套逻辑,而是把这两列的**组合值**当作唯一分组键——(department='IT', gender='M') 和 (department='IT', gender='F') 是两个完全不同的组,哪怕 department 相同。
常见错误现象:执行 SELECT department, gender, COUNT(*) FROM employees GROUP BY department 会报错(如 PostgreSQL/SQL Server 报 column "gender" must appear in the GROUP BY clause),因为 gender 既没参与分组,也没被聚合函数包裹。
- 必须确保
SELECT中所有非聚合列,都完整出现在GROUP BY列表中(顺序无关) - 列名顺序不影响分组结果,但会影响后续
ORDER BY的默认排序倾向 - 多列分组后,每组内所有行在这几列上的值完全一致,其他列值可能不同 → 必须用
COUNT()、MAX()等聚合函数处理
什么时候必须用多列分组
当你需要“在某个维度下再细分”时,比如统计「每个部门里每个性别的员工数」,单靠 GROUP BY department 只能得出部门总数,无法拆解性别结构。
典型使用场景:
- 报表中需要交叉维度汇总(部门 × 职级、地区 × 产品线、日期 × 渠道)
- 去重计数依赖多个字段共同决定唯一性(如
user_id, session_id联合标识一次会话) - 避免因单列值重复导致的分组合并(如两个不同城市的客户都叫“张三”,单按 name 分组会误合并)
示例:SELECT region, product_category, SUM(sales) AS total FROM orders GROUP BY region, product_category —— 这样才能分别看到华东/手机、华东/电脑、华北/手机等独立分组的销售额。
MySQL 的宽松模式是个坑
MySQL 默认允许 SELECT a, b FROM t GROUP BY a(b 未聚合也未出现在 GROUP BY 中),但这不是标准 SQL 行为,且返回的 b 值是该组中任意一行的值,不可预测。
这种写法在 PostgreSQL、SQL Server、Oracle 中直接报错,在迁移或共用 SQL 时极易出问题。
- 开发阶段就应关闭 MySQL 的
ONLY_FULL_GROUP_BY模式,或主动开启它(推荐) - 检查当前模式:
SELECT @@sql_mode;,确认是否含ONLY_FULL_GROUP_BY - 临时启用:
SET sql_mode = 'ONLY_FULL_GROUP_BY';
ORDER BY 和多列分组的配合细节
ORDER BY 可以引用 SELECT 中的别名(如 ORDER BY total DESC),也可以直接写分组列或聚合表达式(如 ORDER BY region, SUM(sales) DESC)。但注意:
- 不能写未出现在
SELECT中、又未聚合的列(如SELECT region, COUNT(*) FROM t GROUP BY region ORDER BY city会失败) - 若排序依据是聚合结果,建议显式命名别名,提升可读性与兼容性
- 多列
ORDER BY的优先级从左到右,和GROUP BY列顺序无关
真正容易被忽略的是:分组本身不保证输出顺序,ORDER BY 是唯一可控手段;而很多开发者依赖 MySQL 默认返回顺序,上线后换数据库就乱序。










