Skip to content

sql-having-clause

SQL HAVING 子句用于根据聚合函数(aggregate functions)过滤查询结果。WHERE 子句在任何分组发生之前过滤单行数据,而 HAVING 子句则在数据通过 GROUP BY 子句聚合之后过滤整个行组。因此,HAVING 子句几乎总是与 GROUP BY 一起使用。

您可以在 HAVING 子句中使用标准的聚合函数,如 COUNT()、SUM()、AVG()、MIN() 和 MAX(),来为数据组指定条件。

对于初学者来说,这常常是一个容易混淆的地方。关键区别在于过滤过程发生的时间点:

  • WHERE 子句在聚合之前过滤行。它作用于单个行数据。
  • HAVING 子句在聚合之后过滤组。它作用于聚合函数的结果。

类比:想象一家餐厅。WHERE 子句就像服务员丢弃不符合某个标准的单个订单(例如,“不要洋葱”)。GROUP BY 子句就像按桌号对所有订单进行分组。HAVING 子句就像经理检查哪些桌子(组)的总账单(一个聚合值)超过 100 美元。

包含 HAVING 子句的查询基本语法如下:

SELECT column_name(s), aggregate_function(column_name)
FROM table_name
WHERE condition(s) -- 可选:在分组前过滤行
GROUP BY column_name(s)
HAVING condition(s) -- 组过滤的必需项
ORDER BY column_name(s); -- 可选:对最终结果集进行排序

理解数据库处理查询的逻辑顺序至关重要,因为这解释了为什么 HAVING 作用于聚合数据而 WHERE 不作用于聚合数据:

1. FROM / JOIN
2. WHERE
3. GROUP BY
4. HAVING
5. SELECT
6. DISTINCT
7. ORDER BY
8. LIMIT / OFFSET / FETCH

让我们使用一个现代的 employees 表作为示例。该表存储员工的详细信息,包括他们的部门和薪资。

-- 设置:创建并填充 'employees' 表
CREATE TABLE employees (
id INT PRIMARY KEY AUTO_INCREMENT,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
department VARCHAR(50) NOT NULL,
salary DECIMAL(10, 2) NOT NULL
);
INSERT INTO employees (first_name, last_name, department, salary) VALUES
('Alice', 'Smith', 'Engineering', 95000.00),
('Bob', 'Johnson', 'Engineering', 110000.00),
('Charlie', 'Brown', 'HR', 60000.00),
('Diana', 'Prince', 'Marketing', 75000.00),
('Ethan', 'Hunt', 'Engineering', 125000.00),
('Fiona', 'Glenanne', 'Marketing', 82000.00),
('George', 'Costanza', 'Sales', 88000.00);

让我们找出员工数量多于一个的部门。

-- 查找员工数量为 2 人或更多的部门
SELECT
department,
COUNT(id) AS number_of_employees
FROM employees
GROUP BY department
HAVING COUNT(id) >= 2;
部门员工数量
Engineering3
Marketing2

将 HAVING 与 SUM() 和 AVG() 结合使用

Section titled “将 HAVING 与 SUM() 和 AVG() 结合使用”

现在,让我们找出平均薪资超过 90,000 美元的部门。

-- 查找平均薪资超过 90,000 美元的部门
SELECT
department,
AVG(salary) AS average_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 90000.00;
部门平均薪资
Engineering110000.00

将 HAVING 与 MIN() 和 MAX() 结合使用

Section titled “将 HAVING 与 MIN() 和 MAX() 结合使用”

此查询查找最高薪资至少为 120,000 美元的部门。

-- 查找至少有一名员工薪资达到或超过 120,000 美元的部门
SELECT
department,
MAX(salary) AS max_salary_in_dept
FROM employees
GROUP BY department
HAVING MAX(salary) >= 120000.00;
部门部门最高薪资
Engineering125000.00

ORDER BY 子句在 HAVING 子句之后应用,允许您对最终过滤后的组进行排序。让我们找出所有部门,计算它们的总薪资支出,然后只显示总支出超过 150,000 美元的部门,并按从高到低的顺序排序。

SELECT
department,
SUM(salary) AS total_payroll
FROM employees
GROUP BY department
HAVING SUM(salary) > 150000.00
ORDER BY total_payroll DESC;
部门总薪资支出
Engineering330000.00
  • 错误: 在 HAVING 中使用不在 GROUP BY 中的非聚合列。这将在大多数 SQL 数据库中导致错误。
  • 错误: 在 HAVING 子句中使用 SELECT 列表中的别名。某些数据库(如 MySQL)支持此操作,但它不符合标准 SQL,在其他地方可能会失败。标准做法是重复聚合函数。
  • 最佳实践: 在分组之前尽可能多地使用 WHERE 进行过滤。WHERE 子句减少了需要由 GROUP BY 和 HAVING 子句处理的行数,这更有效率。例如,如果您只关心“工程”部门的薪资,请首先使用 WHERE department = 'Engineering' 进行过滤。
  • 最佳实践: 保持 HAVING 子句简洁,并专注于聚合条件。复杂的逻辑通常可以使用公用表表达式(CTE)来简化。

想象您是电子商务平台的数据分析师。一项常见的任务是识别高价值客户。您可以使用 HAVING 子句来查找下订单超过 5 个或总消费超过 1,000 美元的客户。

-- 查找高价值客户的伪代码
SELECT
customer_id,
COUNT(order_id) AS total_orders,
SUM(order_value) AS total_spent
FROM orders
GROUP BY customer_id
HAVING COUNT(order_id) > 5 OR SUM(order_value) > 1000.00
ORDER BY total_spent DESC;