Skip to content

SQL Having

在 SQL 中,HAVING 子句用于过滤由 GROUP BY 子句创建的分组。它与 WHERE 子句类似,但有一个关键区别:WHERE 在行被分组和聚合之前过滤单个行,而 HAVING 在聚合函数(如 COUNT()、SUM()、AVG()、MIN()、MAX())应用之后过滤整个分组。

为什么使用 HAVING?因为 WHERE 无法处理聚合结果

你不能直接在 WHERE 子句中使用聚合函数。WHERE 子句作用于行级别的数据。当聚合函数计算时,WHERE 子句的过滤已经完成。HAVING 子句的引入正是为了满足基于聚合函数结果进行过滤的需求。

HAVING 子句通常与 GROUP BY 一起使用:

SELECT column_name(s), aggregate_function(column_name) AS alias_name FROM table_name WHERE condition — Optional: filters rows before grouping GROUP BY column_name(s) HAVING aggregate_condition; — Filters groups after aggregation

  1. FROM 表名
  2. WHERE 条件(过滤行)
  3. GROUP BY 列名(分组行)
  4. HAVING 聚合条件(过滤分组)
  5. SELECT 列名, 聚合函数(选择数据)
  6. ORDER BY 列名(对最终结果排序)

对于我们的示例,假设我们正在使用经典 Northwind 示例数据库的简化版本。我们将重点关注两个表:Orders 和 Employees。

Orders 表结构和数据示例:

OrderIDCustomerIDEmployeeIDOrderDateShipperID
102489051996-07-043
102498161996-07-051
102503441996-07-082
… more orders …

Employees 表结构和数据示例:

EmployeeIDLastNameFirstNameBirthDate
1DavolioNancy1968-12-08
2FullerAndrew1952-02-19
3LeverlingJanet1963-08-30
4PeacockMargaret1958-09-19
5BuchananSteven1955-03-04
6SuyamaMichael1963-07-02
… more employees …

示例 1:查找提交订单数超过 10 的员工。

这里,我们需要统计每个员工的订单数,然后基于该计数进行过滤。

SELECT
E.LastName,
COUNT(O.OrderID) AS NumberOfOrders
FROM Orders AS O
INNER JOIN Employees AS E ON O.EmployeeID = E.EmployeeID
GROUP BY E.LastName
HAVING COUNT(O.OrderID) > 10
ORDER BY NumberOfOrders DESC;
  • INNER JOIN 连接 Orders 表和 Employees 表。
  • GROUP BY E.LastName 根据员工姓氏对订单进行分组。
  • COUNT(O.OrderID) 计算每个员工的订单数。
  • HAVING COUNT(O.OrderID) > 10 过滤这些分组,只保留订单数大于 10 的员工。
  • 使用表别名(O 代表 Orders,E 代表 Employees)使查询更易读。

示例 2:查找员工 ‘Davolio’ 或 ‘Fuller’ 是否每人提交了超过 25 个订单。

这个示例结合了 WHERE 子句(用于预先过滤特定员工)和 HAVING 子句。

SELECT
E.LastName,
COUNT(O.OrderID) AS NumberOfOrders
FROM Orders AS O
INNER JOIN Employees AS E ON O.EmployeeID = E.EmployeeID
WHERE E.LastName IN ('Davolio', 'Fuller') -- 在分组前过滤行
GROUP BY E.LastName
HAVING COUNT(O.OrderID) > 25; -- 在聚合后过滤分组
  • WHERE E.LastName IN ('Davolio', 'Fuller') 首先只选择由 Davolio 或 Fuller 处理的订单。
  • 然后,GROUP BY E.LastName 对这些过滤后的订单进行分组。
  • 最后,HAVING COUNT(O.OrderID) > 25 检查在这些预选的员工中,是否有任何人的订单数超过 25 个。
  • 在聚合条件中使用 WHERE:请记住,WHERE 用于 GROUP BY 之前的行级过滤。HAVING 用于 GROUP BY 之后的分组级过滤。
  • 在没有 GROUP BY 的情况下使用 HAVING:虽然某些 DBMS 可能允许在没有显式 GROUP BY 的情况下使用 HAVING(将整个表视为一个分组),但标准做法是与 GROUP BY 一起使用 HAVING,这样更清晰。
  • 在 HAVING 中使用列别名:标准 SQL 要求在 HAVING 子句中重复聚合函数(例如,HAVING COUNT(O.OrderID) > 10)。然而,许多现代 DBMS(如 MySQL、PostgreSQL)允许使用在 SELECT 列表中定义的别名(例如,HAVING NumberOfOrders > 10)。为了最大限度地提高可移植性,请重复使用函数。
  • 性能:如果条件可以在分组之前应用(即,不涉及聚合函数),通常使用 WHERE 比使用 HAVING 更高效。