我们还没聊过的一个点,就是怎么在用聚合函数之后过滤分组?有时候我们不需要所有学院——只想看学生人数超过一百的那些。或者只关心平均工资高于 50,000 的部门。今天我们就来认识下怎么用 HAVING 过滤聚合后的数据。
既然有 WHERE,为啥还要 HAVING?直接在 GROUP BY 后面加 WHERE 不行吗 :)
没那么简单!首先,SQL 里的操作符顺序是固定的,WHERE 是在 GROUP BY 之前执行的。
那能不能把 WHERE 放到 GROUP BY 后面?
也不行!其实很多时候我们需要在分组前先过滤一波数据,然后再分组,最后再把分组后的结果再过滤一遍。
那是不是可以直接复制 WHERE,改个名字叫 HAVING,放在 GROUP BY 后面?
对,就是这么干的!:)
HAVING 和 WHERE 的区别
WHERE 是在分组前过滤行。
想象下你挑蛋糕吃:草莓味和巧克力味的留下,其他的都不要。这就是 WHERE 的活儿。
HAVING 是在数据分组和聚合函数“施法”之后过滤。
比如你已经把蛋糕按桌子分组,数好了每桌有几个蛋糕,现在只想看蛋糕数大于三的桌子。
所以,HAVING 是用来在分组层面过滤数据的。
HAVING 的语法
语法和 WHERE 差不多,但用法有点不一样:
SELECT 列, 聚合函数
FROM 表
GROUP BY 列
HAVING 条件;
执行步骤:
- 先用
WHERE过滤行。 - 然后用
GROUP BY分组。 - 对分组结果用聚合函数处理。
- 最后用
HAVING过滤分组结果。
HAVING 的用法例子
例子 1:筛选学生人数多的学院
你想知道大学里哪些学院学生人数超过 100。假设有张 students 表:
| id | name | faculty |
|---|---|---|
| 1 | Alice | Engineering |
| 2 | Bob | Engineering |
| 3 | Charlie | Arts |
| 4 | Daisy | Business |
| 5 | ... | ... |
查询:
SELECT faculty, COUNT(*) AS student_count
FROM students
GROUP BY faculty
HAVING COUNT(*) > 100;
这里发生了什么:
- 先用
GROUP BY按faculty分组。 - 然后
COUNT(*)统计每个学院的学生数。 - 最后
HAVING把学生数小于等于 100 的学院都过滤掉。
结果:
| faculty | student_count |
|---|---|
| Engineering | 150 |
| Arts | 120 |
例子 2:平均工资高的部门
你想找出平均工资超过 50,000 的部门。假设有张 employees 表:
| id | name | department | salary |
|---|---|---|---|
| 1 | Alice | IT | 60000 |
| 2 | Bob | HR | 45000 |
| 3 | Charlie | IT | 70000 |
| 4 | Daisy | HR | 52000 |
| 5 | ... | ... | ... |
查询:
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 50000;
结果:
| department | avg_salary |
|---|---|
| IT | 65000 |
注意:HAVING 是在 GROUP BY 之后处理聚合结果的。
WHERE、GROUP BY 和 HAVING 的执行顺序
WHERE 和 HAVING 的过滤是在不同阶段。为了更清楚地理解区别,来看下 SQL 查询的分步流程:
WHERE:过滤行。这一步处理表里的所有行。不满足
WHERE条件的行直接被丢弃,不会进入后续处理。GROUP BY:分组。过滤后的行会根据
GROUP BY指定的列分组。聚合函数:
对分组后的数据用
COUNT()、AVG()、SUM()等聚合函数处理。HAVING:过滤分组。这一步只处理聚合结果。
HAVING的条件只对分组起作用。
HAVING 的特点
特点 1:能用聚合函数
HAVING 和 WHERE 最大的区别就是能用聚合函数。比如:
SELECT department, AVG(salary)
FROM employees
GROUP BY department
HAVING AVG(salary) > 50000;
在这个查询里,AVG(salary) 不能写在 WHERE 里,因为 WHERE 是在分组前处理的。像这样写:
SELECT department, AVG(salary)
FROM employees
WHERE AVG(salary) > 50000
GROUP BY department;
会报错:aggregate functions are not allowed in WHERE。
特点 2:没分组也能用 HAVING
你甚至可以不用 GROUP BY,直接用 HAVING。这种情况下,所有记录会被当成一个分组:
SELECT AVG(salary) AS avg_salary
FROM employees
HAVING AVG(salary) > 50000;
实战例子
假设有家商店,有张销售表 sales:
| id | product_id | sales_amount |
|---|---|---|
| 1 | 101 | 200.00 |
| 2 | 102 | 300.00 |
| 3 | 101 | 400.00 |
| 4 | 103 | 150.00 |
查询:找出总销售额大于 500 的商品。
SELECT product_id, SUM(sales_amount) AS total_sales
FROM sales
GROUP BY product_id
HAVING SUM(sales_amount) > 500;
结果:
| product_id | total_sales |
|---|---|
| 101 | 600.00 |
常见错误
在 WHERE 里用聚合函数:
比如:
SELECT department, AVG(salary)
FROM employees
WHERE AVG(salary) > 50000
GROUP BY department;
错误:WHERE 里不能用聚合函数。
和 NULL 有关的错误:
如果数据里有 NULL,过滤结果可能会很奇怪。比如:
SELECT department, SUM(salary)
FROM employees
GROUP BY department
HAVING SUM(salary) > 0;
如果 salary 这一列全是 NULL,结果可能是 0 或者空的。
恭喜你!现在你已经可以很自信地过滤聚合数据啦!记住,HAVING 是你做分组分析的利器,普通 WHERE 搞不定的场景就靠它了。
GO TO FULL VERSION