SQL Having
SQL HAVING 子句:过滤聚合结果
Section titled “SQL HAVING 子句:过滤聚合结果”HAVING 子句的作用
Section titled “HAVING 子句的作用”在 SQL 中,HAVING 子句用于过滤由 GROUP BY 子句创建的分组。它与 WHERE 子句类似,但有一个关键区别:WHERE 在行被分组和聚合之前过滤单个行,而 HAVING 在聚合函数(如 COUNT()、SUM()、AVG()、MIN()、MAX())应用之后过滤整个分组。
为什么使用 HAVING?因为 WHERE 无法处理聚合结果
你不能直接在 WHERE 子句中使用聚合函数。WHERE 子句作用于行级别的数据。当聚合函数计算时,WHERE 子句的过滤已经完成。HAVING 子句的引入正是为了满足基于聚合函数结果进行过滤的需求。
SQL HAVING 语法
Section titled “SQL 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
执行顺序(简化):
Section titled “执行顺序(简化):”FROM表名WHERE条件(过滤行)GROUP BY列名(分组行)HAVING聚合条件(过滤分组)SELECT列名, 聚合函数(选择数据)ORDER BY列名(对最终结果排序)
示例数据库上下文
Section titled “示例数据库上下文”对于我们的示例,假设我们正在使用经典 Northwind 示例数据库的简化版本。我们将重点关注两个表:Orders 和 Employees。
Orders 表结构和数据示例:
| OrderID | CustomerID | EmployeeID | OrderDate | ShipperID |
|---|---|---|---|---|
| 10248 | 90 | 5 | 1996-07-04 | 3 |
| 10249 | 81 | 6 | 1996-07-05 | 1 |
| 10250 | 34 | 4 | 1996-07-08 | 2 |
| … more orders … |
Employees 表结构和数据示例:
| EmployeeID | LastName | FirstName | BirthDate |
|---|---|---|---|
| 1 | Davolio | Nancy | 1968-12-08 |
| 2 | Fuller | Andrew | 1952-02-19 |
| 3 | Leverling | Janet | 1963-08-30 |
| 4 | Peacock | Margaret | 1958-09-19 |
| 5 | Buchanan | Steven | 1955-03-04 |
| 6 | Suyama | Michael | 1963-07-02 |
| … more employees … |
SQL HAVING 示例
Section titled “SQL HAVING 示例”示例 1:查找提交订单数超过 10 的员工。
这里,我们需要统计每个员工的订单数,然后基于该计数进行过滤。
SELECT E.LastName, COUNT(O.OrderID) AS NumberOfOrdersFROM Orders AS OINNER JOIN Employees AS E ON O.EmployeeID = E.EmployeeIDGROUP BY E.LastNameHAVING COUNT(O.OrderID) > 10ORDER 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 NumberOfOrdersFROM Orders AS OINNER JOIN Employees AS E ON O.EmployeeID = E.EmployeeIDWHERE E.LastName IN ('Davolio', 'Fuller') -- 在分组前过滤行GROUP BY E.LastNameHAVING COUNT(O.OrderID) > 25; -- 在聚合后过滤分组WHERE E.LastName IN ('Davolio', 'Fuller')首先只选择由 Davolio 或 Fuller 处理的订单。- 然后,
GROUP BY E.LastName对这些过滤后的订单进行分组。 - 最后,
HAVING COUNT(O.OrderID) > 25检查在这些预选的员工中,是否有任何人的订单数超过 25 个。
常见学习障碍与最佳实践:
Section titled “常见学习障碍与最佳实践:”- 在聚合条件中使用
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更高效。