where必须写在group by前面,因为sql执行顺序为from→where→group by→having→select→order by,where作用于分组前的原始行,不能含聚合函数,且不识别select别名;having则用于分组后的结果过滤,必须与group by配合使用。

WHERE 必须写在 GROUP BY 前面
SQL 解析器会按固定顺序执行子句,WHERE 是最早生效的筛选环节,它作用于原始行数据。如果把它写在 GROUP BY 后面,语句直接报错:ERROR 1064 (42000) 或类似语法错误。这不是风格问题,是解析器硬性要求。
常见错误写法:
SELECT dept, COUNT(*) FROM emp GROUP BY dept WHERE salary > 5000;
正确写法必须是:
SELECT dept, COUNT(*) FROM emp WHERE salary > 5000 GROUP BY dept;
要点:
-
WHERE条件中不能出现聚合函数,比如WHERE AVG(salary) > 5000会报错 -
WHERE可以用字段别名吗?不能 —— 别名在SELECT阶段才生成,而WHERE在之前就执行了 - 多表 JOIN 时,
WHERE通常放在JOIN ... ON之后、GROUP BY之前
HAVING 只能跟在 GROUP BY 后面
HAVING 不是可选的“加强版 WHERE”,它是专为分组后结果设计的过滤器。没有 GROUP BY,HAVING 就没有意义,MySQL 会直接拒绝执行。
典型误用场景:
- 想筛平均值却写成
WHERE AVG(score) > 85→ 报错:invalid use of aggregate function - 漏写
GROUP BY,只写HAVING COUNT(*) > 1→ 报错:Unknown column 'COUNT(*)' in 'having clause'
正确示例(查平均分超 85 的班级):
SELECT class_id, AVG(score) FROM student_scores GROUP BY class_id HAVING AVG(score) > 85;
注意:HAVING 中可用的列,只能是 GROUP BY 中出现的列,或聚合函数(如 COUNT()、MAX()),不能是未分组也未聚合的普通字段。
WHERE 和 HAVING 联合使用的实际逻辑
二者分工明确:WHERE 先筛“人”,GROUP BY 再聚“堆”,HAVING 最后筛“堆”。例如查“上海地区每本书平均售价超 20 元的出版商”:
SELECT p.pub_name, AVG(t.price) FROM titles t JOIN publishers p ON t.pub_id = p.pub_id WHERE p.city = 'Shanghai' GROUP BY p.pub_name HAVING AVG(t.price) > 20;
执行过程拆解:
-
WHERE p.city = 'Shanghai':先剔除所有非上海出版商的记录(减少后续分组数据量) -
GROUP BY p.pub_name:把剩下的记录按出版商归堆 -
HAVING AVG(t.price) > 20:对每堆算平均价,只留平均价超 20 的堆
性能提示:把高选择性条件(如状态码、时间范围)尽量放 WHERE,避免无谓分组;低选择性但依赖聚合结果的条件(如“销量 Top 10”、“平均响应时长 > 2s”)必须放 HAVING。
GROUP BY 字段和 SELECT 字段的匹配规则
MySQL 5.7+ 默认开启 sql_mode=only_full_group_by,这意味着:SELECT 列表里每个非聚合字段,都必须出现在 GROUP BY 子句中。
比如下面这句会报错:
SELECT dept, name, COUNT(*) FROM emp GROUP BY dept;
因为 name 没参与分组,也没被聚合,数据库不知道该取哪一行的 name 值。
修复方式只有两种:
- 把
name加进GROUP BY(变成按 dept + name 分组) - 用聚合函数包裹
name,比如MAX(name)或GROUP_CONCAT(name)
这个限制常被忽略,尤其在从旧版本迁移或用 ORM 自动生成 SQL 时,容易触发 ERROR 1055。










