mysql严格模式下group by必须包含select中所有非聚合字段;where过滤行、having过滤组;null在group by中被视作同一组;count(字段)跳过null而count(*)不跳。

GROUP BY 语句必须包含在 SELECT 中所有非聚合字段
MySQL 严格模式下,SELECT 列表里只要用了 GROUP BY,每个非聚合表达式(比如 name、status)都得出现在 GROUP BY 子句里,否则报错:Expression #1 of SELECT list is not in GROUP BY clause。
常见错误是写成这样:
SELECT name, COUNT(*) FROM users GROUP BY status;
这会直接失败——name 既没被聚合,也没参与分组。解决办法只有两个:
- 把
name加进GROUP BY:GROUP BY status, name(适合想看每种 status 下每个 name 的统计) - 去掉
name,只留聚合字段,或改用聚合函数包裹:MAX(name)、GROUP_CONCAT(name)(适合只需要代表值)
WHERE 和 HAVING 的分工不能颠倒
WHERE 过滤的是“行”,HAVING 过滤的是“组”。想筛分组前的数据,用 WHERE;想筛分组后的结果(比如“订单数大于 5 的用户”),必须用 HAVING。
典型误用:
SELECT user_id, COUNT(*) FROM orders WHERE COUNT(*) > 5 GROUP BY user_id;
这是语法错误——COUNT(*) 在 WHERE 阶段还不存在。正确写法是:
PHP是一种功能强大的网络程序设计语言,而且易学易用,移植性和可扩展性也都非常优秀,本书将为读者详细介绍PHP编程。 全书分为预备篇、开始篇和加速篇三大部分,共9章。预备篇主要介绍一些学习PHP语言的预备知识以及PHP运行平台的架设;开始篇则较为详细地向读者介绍PKP语言的基本语法和常用函数,以及用PHP如何对MySQL数据库进行操作;加速篇则通过对典型实例的介绍来使读者全面掌握PHP。 本书
SELECT user_id, COUNT(*) FROM orders GROUP BY user_id HAVING COUNT(*) > 5;
-
WHERE可以用索引加速,HAVING是对临时结果集二次扫描,性能更差 - 如果既要行级过滤又要组级筛选,
WHERE一定要写在GROUP BY前,HAVING写在后
NULL 值在 GROUP BY 中会被当作同一组
MySQL 把所有 NULL 当作相等值处理,所以 GROUP BY category 时,所有 category IS NULL 的记录会挤进同一个分组。
这不是 bug,是标准行为,但容易被忽略:
- 如果你的业务里
NULL表示“未分类”,而你又想把它和“已分类但值为空字符串”区分开,就得提前用COALESCE(category, 'UNSET')或CASE WHEN显式归类 - 聚合函数如
COUNT(category)会跳过NULL,但COUNT(*)不会——注意统计口径差异
ORDER BY 中引用别名可能出错
MySQL 允许在 ORDER BY 里用 SELECT 列表中的别名,比如:
SELECT status, COUNT(*) AS cnt FROM users GROUP BY status ORDER BY cnt DESC;
但要注意:这个别名只在 ORDER BY 有效,不能在 WHERE 或 HAVING 里用。更关键的是,如果同时有多个同名列(比如子查询里也叫 cnt),MySQL 可能解析错作用域。
- 稳妥做法是直接写表达式:
ORDER BY COUNT(*) DESC - 如果表达式复杂(比如
ROUND(AVG(score), 2)),重复写两遍太啰嗦,就用子查询或 CTE 包一层,避免歧义
GROUP BY 看似简单,真正卡住人的往往不是语法,而是 NULL 处理、执行顺序误解、还有聚合字段和分组字段的隐含绑定关系——这些地方一松手,结果就偏了。









