HAVING:分组后过滤
WHERE vs HAVING
两者都用于过滤数据,但作用时机不同:
- WHERE:在分组之前过滤行(基于原始数据)
- HAVING:在分组之后过滤组(基于聚合结果)
示例对比:
sql
-- WHERE:只统计 Electronics 订单
SELECT
customer_name,
COUNT(*) AS orders
FROM orders
WHERE product_category = 'Electronics'
GROUP BY customer_name;结果(过滤后再分组):
text
customer_name | orders
---------------+--------
Alice | 2
Bob | 1
Charlie | 1sql
-- HAVING:只显示订单数 > 1 的客户
SELECT
customer_name,
COUNT(*) AS orders
FROM orders
GROUP BY customer_name
HAVING COUNT(*) > 1;结果(分组后再过滤):
text
customer_name | orders
---------------+--------
Alice | 3
Bob | 2
Charlie | 2HAVING 基础语法
sql
SELECT column, aggregate_function(column)
FROM table
GROUP BY column
HAVING aggregate_condition;HAVING 条件通常包含聚合函数。
HAVING 的使用场景
场景 1:过滤聚合结果
sql
-- 查询总消费超过 500 的客户
SELECT
customer_name,
SUM(amount) AS total_spent
FROM orders
GROUP BY customer_name
HAVING SUM(amount) > 500;结果:
text
customer_name | total_spent
---------------+-------------
Alice | 1525.00场景 2:过滤计数结果
sql
-- 查询订单数量 >= 2 的客户
SELECT
customer_name,
COUNT(*) AS order_count
FROM orders
GROUP BY customer_name
HAVING COUNT(*) >= 2
ORDER BY order_count DESC;结果:
text
customer_name | order_count
---------------+-------------
Alice | 3
Bob | 2
Charlie | 2场景 3:过滤平均值
sql
-- 查询平均订单金额超过 200 的客户
SELECT
customer_name,
COUNT(*) AS orders,
ROUND(AVG(amount), 2) AS average_order
FROM orders
GROUP BY customer_name
HAVING AVG(amount) > 200;结果:
text
customer_name | orders | average_order
---------------+--------+---------------
Alice | 3 | 508.33WHERE 和 HAVING 结合使用
可以同时使用 WHERE 和 HAVING:
sql
-- WHERE:先过滤 2026-01-17 之后的订单
-- HAVING:再过滤订单数 >= 2 的客户
SELECT
customer_name,
COUNT(*) AS orders,
SUM(amount) AS total
FROM orders
WHERE order_date >= '2026-01-17'
GROUP BY customer_name
HAVING COUNT(*) >= 2;执行顺序:
- FROM:获取 orders 表
- WHERE:过滤日期
- GROUP BY:按客户分组
- HAVING:过滤组
- SELECT:选择列
- ORDER BY:排序(如果有)
HAVING 中的条件
可以使用的条件
HAVING 可以包含:
- 聚合函数(最常见)
sql
HAVING COUNT(*) > 2
HAVING SUM(amount) > 1000
HAVING AVG(amount) >= 100- 分组列(较少见,但合法)
sql
SELECT
product_category,
COUNT(*) AS orders
FROM orders
GROUP BY product_category
HAVING product_category IN ('Electronics', 'Furniture');注意:上面的查询用 WHERE 更高效:
sql
-- 更好的写法
SELECT
product_category,
COUNT(*) AS orders
FROM orders
WHERE product_category IN ('Electronics', 'Furniture')
GROUP BY product_category;- 组合条件
sql
SELECT
customer_name,
COUNT(*) AS orders,
SUM(amount) AS total
FROM orders
GROUP BY customer_name
HAVING COUNT(*) >= 2 AND SUM(amount) > 300;不能使用的条件
HAVING 不能直接引用 SELECT 中的列别名(部分数据库例外):
sql
-- 错误(PostgreSQL):order_count 还未计算
SELECT
customer_name,
COUNT(*) AS order_count
FROM orders
GROUP BY customer_name
HAVING order_count > 2; -- 错误!正确写法:
sql
SELECT
customer_name,
COUNT(*) AS order_count
FROM orders
GROUP BY customer_name
HAVING COUNT(*) > 2; -- 正确MySQL 例外:MySQL 允许在 HAVING 中使用列别名:
sql
-- MySQL 支持
SELECT
customer_name,
COUNT(*) AS order_count
FROM orders
GROUP BY customer_name
HAVING order_count > 2;但为了跨数据库兼容,建议使用原始聚合函数表达式。
HAVING 与子查询对比
某些 HAVING 查询可以用子查询替代:
使用 HAVING:
sql
SELECT
customer_name,
SUM(amount) AS total
FROM orders
GROUP BY customer_name
HAVING SUM(amount) > (SELECT AVG(total_per_customer)
FROM (SELECT SUM(amount) AS total_per_customer
FROM orders
GROUP BY customer_name) AS t);虽然复杂,但 HAVING 通常更简洁和高效。
实际应用示例
示例 1:找出高价值客户
sql
-- 定义:总消费 > 1000 或订单数 >= 3
SELECT
customer_name,
COUNT(*) AS orders,
SUM(amount) AS total_spent
FROM orders
GROUP BY customer_name
HAVING SUM(amount) > 1000 OR COUNT(*) >= 3
ORDER BY total_spent DESC;示例 2:找出异常订单模式
sql
-- 平均订单金额特别高的客户(可能是企业客户)
SELECT
customer_name,
COUNT(*) AS orders,
ROUND(AVG(amount), 2) AS avg_order
FROM orders
GROUP BY customer_name
HAVING AVG(amount) > 300
ORDER BY avg_order DESC;示例 3:分类汇总与过滤
sql
-- 每个类别中,订单数 >= 2 的客户
SELECT
product_category,
customer_name,
COUNT(*) AS orders,
SUM(amount) AS total
FROM orders
GROUP BY product_category, customer_name
HAVING COUNT(*) >= 2
ORDER BY product_category, total DESC;WHERE vs HAVING 选择指南
| 条件类型 | 使用 WHERE | 使用 HAVING |
|---|---|---|
| 基于原始列 | ✅ | ❌(用 WHERE 更高效) |
| 基于聚合结果 | ❌(不允许) | ✅ |
| 执行时机 | 分组前 | 分组后 |
| 性能 | 更快(减少分组数据) | 较慢(先分组再过滤) |
规则:
- 能用 WHERE 就用 WHERE(更高效)
- 只有在需要过滤聚合结果时才用 HAVING
常见错误
错误 1:在 WHERE 中使用聚合函数
sql
-- 错误
SELECT customer_name, SUM(amount)
FROM orders
WHERE SUM(amount) > 1000 -- 错误!
GROUP BY customer_name;正确写法:
sql
SELECT customer_name, SUM(amount)
FROM orders
GROUP BY customer_name
HAVING SUM(amount) > 1000;错误 2:HAVING 用于非聚合条件
sql
-- 低效
SELECT customer_name, COUNT(*)
FROM orders
GROUP BY customer_name
HAVING customer_name = 'Alice';更好的写法(用 WHERE):
sql
SELECT customer_name, COUNT(*)
FROM orders
WHERE customer_name = 'Alice'
GROUP BY customer_name;错误 3:忘记 GROUP BY
sql
-- 错误:HAVING 需要 GROUP BY
SELECT customer_name, SUM(amount)
FROM orders
HAVING SUM(amount) > 1000;练习
使用 orders 表完成以下练习:
基本 HAVING:查询订单数量超过 2 的客户。
金额过滤:查询总消费金额超过 200 的客户,显示客户名称和总金额。
平均值过滤:查询平均订单金额超过 150 的客户。
WHERE + HAVING:查询 Electronics 类别中,订单数量 >= 2 的客户。
多条件 HAVING:查询订单数量 >= 2 且总金额 >= 300 的客户。
分类统计:查询每个产品类别中订单数量最多的前 2 名客户(提示:子查询或窗口函数)。
对比查询:分别用 WHERE 和 HAVING 实现"查询 Alice 的订单统计",观察两者的差异。
实际场景:查询每个类别中,平均订单金额超过该类别所有订单平均金额的客户(提示:需要子查询)。
完成练习后,打开本章答案页核对。
下一步
学会了 HAVING 过滤分组。下一节学习多列分组和 ROLLUP:如何生成小计和总计。