group by 后不能用 where 筛选聚合结果,必须用 having;having 在 group by 后、order by 前,可引用分组字段或聚合表达式;order by 可用 select 中定义的别名;复杂二次筛选优先用窗口函数。

GROUP BY 后怎么用 WHERE 筛选?不行,得换 HAVING
直接在 GROUP BY 后加 WHERE 会报错,因为 WHERE 执行在分组前,无法访问聚合结果(比如 COUNT(*)、AVG(price))。真正该用的是 HAVING —— 它专为过滤分组后的结果而生。
常见错误现象:ERROR: column "count" does not exist 或 aggregate functions are not allowed in WHERE。
-
HAVING必须跟在GROUP BY后,且只能引用分组字段或聚合表达式 - 不能写
HAVING price > 100(除非price在GROUP BY中,否则无意义) - 可以写
HAVING COUNT(*) >= 5、HAVING AVG(salary) > 8000
ORDER BY 要放在 HAVING 之后,且能用别名
ORDER BY 在逻辑执行顺序上排在 HAVING 之后,所以它既能按分组字段排序,也能按聚合结果排序。更关键的是:只要在 SELECT 中定义了列别名,ORDER BY 就可以直接用这个别名——不用重复写整个表达式。
使用场景:查出“订单数超 10 的用户”,再按平均订单金额从高到低排。
- 写
ORDER BY avg_amount DESC比ORDER BY AVG(order_amount) DESC更简洁、可读性更好 - 注意:MySQL 允许
ORDER BY引用未出现在SELECT中的字段(只要在GROUP BY中),但 PostgreSQL 和 SQL Server 会报错,需显式包含 - 如果排序字段是聚合结果,确保它没被
HAVING过滤掉(比如HAVING SUM(x) > 0后还能按SUM(x)排序)
二次筛选要嵌套?不一定,窗口函数更灵活
当“二次筛选”不只是简单聚合过滤(比如要取每组销量前三的商品),HAVING 就不够用了。这时候优先考虑窗口函数,而不是套子查询或 CTE —— 多数现代数据库(PostgreSQL、SQL Server、MySQL 8.0+、Oracle)都支持。
性能影响:窗口函数通常比相关子查询或自连接快,且语义更清晰。
- 用
ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC)给每组内记录编号 - 外层用
WHERE rn 即可拿到各品类 Top 3,无需嵌套 <code>GROUP BY - 注意:
RANK()和DENSE_RANK()在有并列时行为不同,按需选择
MySQL 5.7 不支持窗口函数?那就用变量模拟
老版本 MySQL(如 5.7)不支持窗口函数,但可以用用户变量实现类似效果。不过得小心执行顺序和变量初始化问题——这是最容易踩坑的地方。
常见错误现象:编号全为 1,或编号乱序,多因未强制排序或变量未重置。
- 必须搭配
ORDER BY使用,且变量赋值语句要放在SELECT列表中(不是WHERE或HAVING) - 先
@group := category判断分组变化,再@rn := IF(@group = category, @rn + 1, 1) - 务必在外部再套一层查询,避免优化器打乱顺序;或者用
SELECT ... FROM (SELECT ...) t ORDER BY category, sales DESC显式控制
复杂点不在语法本身,而在变量状态是否可控——稍不留神就跨组污染。真要长期维护,建议升级到 MySQL 8.0 或换用支持窗口函数的引擎。











