课程 4.3 · 阅读时间:约 7 分钟
在使用 GROUP BY 和聚合时,您通常需要根据聚合结果过滤分组。HAVING 是用于过滤分组的运算符,就像 WHERE 过滤单独的行一样。在本课中,您将学习 WHERE 和 HAVING 之间的区别、语法以及在 Sakila 上的实际示例。到最后,您将自信地使用 HAVING 进行深入的数据分析。
使用 HAVING 过滤分组
在之前的课程中,您学习了如何使用 GROUP BY 对数据进行分组并应用聚合。现在进入下一步:根据聚合值的条件过滤分组本身。
HAVING 通过允许在分组后添加条件来实现这一点。
WHERE 与 HAVING
- WHERE 在分组 之前 过滤行
- HAVING 在聚合 之后 过滤分组
SELECT column1, COUNT(*) AS cnt
FROM table
WHERE column1 > 100 -- 在分组之前过滤
GROUP BY column1
HAVING COUNT(*) > 10; -- 在分组之后过滤
HAVING 语法
基本结构:
SELECT column1, AGG_FUNCTION(column2)
FROM table
GROUP BY column1
HAVING condition;
HAVING 中的条件通常涉及一个聚合函数。
单条件示例
总支付超过 100 的客户
SELECT customer_id, SUM(amount) AS total_paid
FROM payment
GROUP BY customer_id
HAVING SUM(amount) > 100;
结果:仅包括总支付超过 100 的客户。
处理超过 50 笔支付的员工
SELECT staff_id, COUNT(*) AS payments_count
FROM payment
GROUP BY staff_id
HAVING COUNT(*) > 50;
结果:处理超过 50 笔支付的员工。
平均支付 ≥ 5 的客户
SELECT customer_id, AVG(amount) AS avg_payment
FROM payment
GROUP BY customer_id
HAVING AVG(amount) >= 5;
结果:平均支付至少为 5 的客户。
使用多个条件的 HAVING
您可以使用 AND 和 OR 组合条件:
SELECT staff_id, COUNT(*) AS cnt, SUM(amount) AS total
FROM payment
GROUP BY staff_id
HAVING COUNT(*) > 50 AND SUM(amount) > 500;
结果:处理超过 50 笔支付且总额超过 500 的员工。
使用 OR 运算符
SELECT customer_id, COUNT(*) AS rentals, SUM(amount) AS paid
FROM payment
GROUP BY customer_id
HAVING COUNT(*) > 100 OR SUM(amount) > 1000;
结果:支付超过 100 笔或总额超过 1000 的客户。
实际示例
销售额超过 2000 的电影类别
SELECT category_id, SUM(p.amount) AS total_sales
FROM payment p
JOIN rental r ON p.rental_id = r.rental_id
JOIN inventory i ON r.inventory_id = i.inventory_id
JOIN film_category fc ON i.film_id = fc.film_id
GROUP BY category_id
HAVING SUM(p.amount) > 2000;
客户超过 20 的国家
SELECT country, COUNT(*) AS customers_count
FROM customer cu
JOIN address a ON cu.address_id = a.address_id
JOIN city ci ON a.city_id = ci.city_id
JOIN country co ON ci.country_id = co.country_id
GROUP BY country
HAVING COUNT(*) > 20;
日收入超过 500 的商店
SELECT store_id, DATE(payment_date) AS pay_date, SUM(amount) AS daily_revenue
FROM payment
GROUP BY store_id, DATE(payment_date)
HAVING SUM(amount) > 500;
常见问题解答
为什么我不能用 WHERE 替代 HAVING?
WHERE 在分组之前操作,因此无法检查聚合函数。HAVING 在分组之后操作,可以分析聚合结果(COUNT、SUM、AVG 等)。
我可以在 HAVING 中使用非聚合列吗?
是的,您可以使用来自 GROUP BY 的列,但通常不需要。例如,HAVING customer_id > 100 是有效的,但在分组之前在 WHERE 中写更自然。
HAVING 可以在没有 GROUP BY 的情况下使用吗?
在某些数据库管理系统中技术上是可能的,但不实用,因为 HAVING 是为过滤分组而设计的。使用 WHERE 在不分组的情况下过滤单独的行。
面试问题
什么是 HAVING,它与 WHERE 有何不同?
HAVING 在聚合后过滤分组,而 WHERE 在分组之前过滤行。WHERE 不能与聚合函数一起使用,但 HAVING 只能与它们一起使用。
我可以在一个查询中同时使用 WHERE 和 HAVING 吗?
是的,这甚至是推荐的。WHERE 在分组之前过滤行,而 HAVING 在分组之后过滤分组。例如,WHERE amount > 10 GROUP BY customer_id HAVING SUM(amount) > 100 首先排除小额支付,然后进行分组并过滤分组。
执行顺序是怎样的:WHERE 还是 HAVING?
WHERE 首先应用(在 GROUP BY 之前),然后 GROUP BY 进行分组,然后 HAVING 过滤结果分组,最后应用 ORDER BY 和 LIMIT。
本课的关键要点:
HAVING在聚合 之后 过滤分组,WHERE在分组 之前 过滤行。HAVING与聚合函数(COUNT、SUM、AVG、MIN、MAX)一起使用。- 您可以在
HAVING中使用AND/OR组合多个条件。 - 通常 WHERE 和 HAVING 一起工作:WHERE 排除不需要的行,HAVING 过滤分组。
HAVING使得具有分组级别过滤的深度分析查询成为可能。
在下一课中,我们将探讨 ORDER BY 用于排序结果。