🙏 感谢您的支持! 我们在七月份已经筹集了 $65 — 这足够我们工作到下个月。请帮助我们保持进度,进一步支持这个项目。 支持这个项目 →
SQL 代码已复制到剪贴板

课程 4.3 · 阅读时间:约 7 分钟

在使用 GROUP BY 和聚合时,您通常需要根据聚合结果过滤分组。HAVING 是用于过滤分组的运算符,就像 WHERE 过滤单独的行一样。在本课中,您将学习 WHEREHAVING 之间的区别、语法以及在 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

您可以使用 ANDOR 组合条件:

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 用于排序结果。

尝试解决以下任务,以巩固您在本课中学到的内容。

  1. 重复的演员名字
  2. 影片类别与较长的平均时长
  3. 重复的演员姓氏
  4. 有价值的员工