group by后select列必须为分组列或聚合函数,否则严格模式下报错;where过滤原始行,having过滤分组结果;null在group by中被归为一组;聚合函数不可嵌套;跨数据库需注意模式差异。

GROUP BY 后 SELECT 列必须是分组列或聚合函数
这是最常报错的点:SELECT col1, col2 FROM t GROUP BY col1 在严格模式下直接报错,因为 col2 既没在 GROUP BY 里,也没套聚合函数。MySQL 5.7+ 默认启用 ONLY_FULL_GROUP_BY,PostgreSQL 和 SQL Server 一直严格遵循这条规则。
实操建议:
- 检查当前 SQL 模式:执行
SELECT @@sql_mode,确认是否含ONLY_FULL_GROUP_BY - 要么把所有非聚合列加进
GROUP BY(如GROUP BY col1, col2),要么用聚合函数包裹(如MAX(col2)、ANY_VALUE(col2)) -
ANY_VALUE()是 MySQL 特有函数,不推荐用于业务关键逻辑——它不保证返回哪一行的值,只是绕过校验
WHERE 和 HAVING 混用时的执行顺序搞反了
WHERE 过滤的是原始行,HAVING 过滤的是分组后的结果。写成 WHERE COUNT(*) > 1 肯定报错,因为 COUNT 是聚合函数,还没到 HAVING 阶段。
实操建议:
- 想筛“订单数大于 5 的用户”,用
HAVING COUNT(*) > 5,不能放WHERE - 想筛“2024 年的订单再按用户分组”,时间条件必须写在
WHERE(如WHERE order_time >= '2024-01-01'),否则会先分组再过滤,性能差还可能漏数据 - PostgreSQL 不允许
HAVING引用未出现在SELECT或GROUP BY中的列,MySQL 相对宽松但行为不可靠
NULL 值在 GROUP BY 中被当成同一组
GROUP BY col 时,所有 NULL 值会被归为一组——这不是 bug,是 SQL 标准行为。但很多人误以为 NULL 会被忽略或单独分组失败。
实操建议:
- 如果业务上需要区分“空字符串”和“NULL”,提前用
COALESCE(col, 'NULL_VAL')或CASE WHEN col IS NULL THEN 'unknown' ELSE col END处理 - 想排除 NULL 再分组?加
WHERE col IS NOT NULL,别指望HAVING能筛掉它们 - 某些 ORM(如 Django ORM)生成的
GROUP BY语句可能隐式包含IS NULL判断,注意看最终 SQL
聚合函数嵌套导致语法错误或逻辑错误
SUM(AVG(x))、MAX(COUNT(*)) 这类写法在标准 SQL 中非法——聚合函数不能直接嵌套。有人想“取每组平均值的最大值”,得用子查询或 CTE。
实操建议:
- 想算“各城市平均订单金额的最高值”,写两层:
SELECT MAX(avg_amt) FROM (SELECT AVG(amount) AS avg_amt FROM orders GROUP BY city) t - 窗口函数能简化部分场景,比如
AVG(amount) OVER (PARTITION BY city)可以避免先分组再聚合,但注意它不减少行数 - SQLite 不支持窗口函数(3.25+ 才开始支持),MySQL 8.0+、PostgreSQL 9.4+ 支持较完整
GROUP BY 看似简单,但实际出问题往往不是语法写错,而是对“分组时机”“NULL 归类规则”“聚合作用域”的理解偏差。尤其跨数据库迁移时,MySQL 的宽松模式容易掩盖问题,一换 PostgreSQL 就崩。










