GROUP BY单列时SELECT非聚合字段须与GROUP BY字段一致,否则报错;多列GROUP BY顺序不影响分组逻辑但影响索引和排序;WHERE过滤行、HAVING过滤组;NULL在GROUP BY中被视作相同值归为一组。

GROUP BY 单列时,SELECT 中的非聚合字段必须和 GROUP BY 字段一致
很多初学者写 SELECT name, COUNT(*) FROM users GROUP BY age 会报错(如 MySQL 8.0+ 或 PostgreSQL),因为 name 既没在 GROUP BY 中,也不是聚合函数结果。数据库无法确定该取哪一行的 name。
正确做法是:要么把 name 加进 GROUP BY,要么用聚合函数包裹,比如 MAX(name) 或 STRING_AGG(name, ', ')(PostgreSQL)/ GROUP_CONCAT(name)(MySQL)。
- MySQL 5.7 默认开启
sql_mode=ONLY_FULL_GROUP_BY,强制校验;关闭它只是绕过问题,不解决语义歧义 - PostgreSQL 和 SQL Server 始终严格遵循标准,不支持“隐式选择”
- 如果真想按
age分组并取每个组里第一个name,得用窗口函数,不是靠GROUP BY解决
多列 GROUP BY 的顺序不影响分组结果,但影响排序和索引利用
GROUP BY city, department 和 GROUP BY department, city 分组逻辑完全一样——只要两列值组合相同,就归为同一组。但实际执行中:
- 数据库优化器可能更倾向使用
(city, department)的复合索引,而非反过来的顺序 - 若后续加
ORDER BY city, department,和GROUP BY列序一致时,可能省掉一次排序操作 - 某些旧版 SQLite 或嵌入式引擎对多列顺序敏感,但主流数据库(PostgreSQL、MySQL、SQL Server)无此限制
WHERE 和 HAVING 的分工必须分清:过滤行用 WHERE,过滤组用 HAVING
WHERE 在分组前生效,HAVING 在分组后生效。写反会导致逻辑错误或性能浪费。
- 想查“每部门平均薪资超 15000 的部门”,必须写
HAVING AVG(salary) > 15000;写成WHERE AVG(salary) > 15000直接报错,因为AVG()是聚合函数,不能在WHERE中用 - 想查“只统计 2023 年入职的员工”,必须用
WHERE hire_date >= '2023-01-01';放在HAVING里虽然语法可能通过(如 MySQL 允许非标准写法),但会先分组再过滤,白白计算了所有年份的数据 - 复杂条件可同时存在:
WHERE status = 'active' HAVING COUNT(*) > 5
NULL 值在 GROUP BY 中被当作相同值处理,但容易被忽略
所有 NULL 会被归到同一个组——这点和 ORDER BY 中 NULLS FIRST/LAST 不同,GROUP BY 没有显式控制方式。
- 如果
department有大量NULL,GROUP BY department会产出一个 “department = NULL” 的组,且COUNT(*)统计的是这些空值行数 - 想排除空值再分组?加
WHERE department IS NOT NULL - 想单独标记空值组但又不想漏数据?可用
CASE WHEN department IS NULL THEN 'Unknown' ELSE department END转换后再分组
多列组合中只要有一列是 NULL,整行仍参与分组;但 (NULL, 'A') 和 (NULL, 'B') 是不同组——只有对应位置都为 NULL 才算相等。

















