在MySQL查询中,当我们需要对数据先分组再进行条件筛选时,无法直接使用WHERE子句实现,因为WHERE是在分组前对单行数据进行过滤,而组级别的过滤需要在分组完成后对分组结果进行判断,这时候就需要使用HAVING子句来完成。

HAVING子句的基本用法
HAVING子句需要和GROUP BY子句配合使用,它会在数据分组之后,对每个分组的结果进行条件判断,只保留符合HAVING条件的分组。基本语法结构如下:
SELECT 分组字段, 聚合函数(字段) FROM 表名 GROUP BY 分组字段 HAVING 过滤条件;
比如我们有一张订单表order_info,记录了不同用户的订单金额,现在需要查询订单总金额超过1000的用户,就可以使用以下查询:
-- 按用户ID分组,统计每个用户的订单总金额,筛选总金额大于1000的分组 SELECT user_id, SUM(order_amount) AS total_amount FROM order_info GROUP BY user_id HAVING total_amount > 1000;
WHERE和HAVING的核心区别
很多用户会混淆这两个子句的作用,我们可以通过下表清晰对比两者的差异:
| 对比项 | WHERE子句 | HAVING子句 |
|---|---|---|
| 作用时机 | 分组前对单行数据过滤 | 分组后对分组结果过滤 |
| 是否可以使用聚合函数 | 不可以 | 可以 |
| 是否可以使用别名 | 不可以 | 可以(MySQL扩展支持) |
| 适用场景 | 筛选参与分组的原始数据 | 筛选最终的分组结果 |
结合聚合函数的组级别过滤
HAVING子句最常用的场景就是结合聚合函数进行组级别判断,常见的聚合函数包括SUM、COUNT、AVG、MAX、MIN等。比如我们需要查询订单数量超过5笔,且平均订单金额大于200的用户,查询语句如下:
SELECT user_id,
COUNT(order_id) AS order_count,
AVG(order_amount) AS avg_amount
FROM order_info
GROUP BY user_id
HAVING order_count > 5 AND avg_amount > 200;
多条件组级别过滤
HAVING子句也支持多个条件组合,使用AND或者OR连接即可。比如我们需要查询订单总金额大于1000,或者最大订单金额超过500的用户,语句如下:
SELECT user_id,
SUM(order_amount) AS total_amount,
MAX(order_amount) AS max_amount
FROM order_info
GROUP BY user_id
HAVING total_amount > 1000 OR max_amount > 500;
注意事项
- HAVING子句必须跟在GROUP BY子句之后,如果没有GROUP BY子句,MySQL会将整个结果集作为一个分组,这时候HAVING的作用和WHERE类似,但不建议这样使用。
- 在HAVING子句中引用的字段要么是GROUP BY的分组字段,要么是聚合函数的参数,否则查询结果可能不符合预期。
- 如果需要对分组前的原始数据做筛选,应该把条件放在WHERE子句中,这样可以减少参与分组的数据量,提升查询效率,比如我们需要先筛选2024年之后的订单,再统计用户总金额,就可以这样写:
-- 先过滤2024年后的订单,再分组统计 SELECT user_id, SUM(order_amount) AS total_amount FROM order_info WHERE order_date >= '2024-01-01' GROUP BY user_id HAVING total_amount > 1000;
需要注意的是,标准SQL中HAVING子句不能直接使用SELECT中的字段别名,MySQL作为扩展支持了这个特性,如果需要在其他数据库中使用,建议直接使用聚合表达式或者字段名。