咱们再来梳理一下 SQL 里操作的执行顺序,还有 WHERE 和 HAVING 能干啥、不能干啥。这点很重要,后面很多 SQL 查询的细节都靠它。你得真的搞明白。
那到底 WHERE 和 HAVING 有啥区别,啥时候用哪个,它们怎么配合?搞清楚这个你写查询就不容易绕晕,还能高效过滤数据。来,总结下咱们对 WHERE 和 HAVING 的理解。
啥是 WHERE?
WHERE —— 这是个条件,用来在分组或聚合函数之前先过滤行。也就是说,先从表里挑出符合条件的行,然后才在剩下的数据上做分组。
👉 想象你在市场上挑水果。WHERE 就像你提前把烂苹果挑掉,还没开始按大小或颜色分组呢。
例子:
SELECT *
FROM students
WHERE age > 18;
这个查询会把所有大于 18 岁的学生选出来,在做其他操作之前。
啥是 HAVING?
HAVING —— 这是个过滤器,在数据分组(GROUP BY)之后用的。它能给分好组的数据加条件,比如只保留平均分大于 80 的学生组。
👉 还是苹果的例子。HAVING 就是你已经把苹果分好篮子(分组)后,现在只关心那些篮子里有十个以上苹果的。
例子:
SELECT 篮子, COUNT(*)
FROM 苹果
GROUP BY 篮子
HAVING COUNT(*) > 10;
这个查询只会选出那些苹果数量大于 10 的篮子。
主要区别:
| 特点 | WHERE |
HAVING |
|---|---|---|
| 应用时机 | 分组前过滤行 | 分组后过滤组 |
| 聚合函数 | 不能用聚合函数 | 能用聚合函数 |
| 目的 | 把不需要的行先去掉再分组 | 把不满足条件的组去掉 |
WHERE、GROUP BY 和 HAVING 的执行顺序
为了更好理解 WHERE 和 HAVING 怎么工作,咱们看看 SQL 查询的执行顺序:
- 先执行
FROM,从表里选出行。 - 然后用
WHERE,只留下符合条件的行。 - 接着用
GROUP BY分组。得到新的分组数据表。 - 用
HAVING,只保留满足条件的组。 - 最后选出
SELECT的结果。
流程图大概这样:
1. FROM → 2. WHERE → 3. GROUP BY → 4. HAVING → 5. SELECT
例子:
SELECT department, AVG(age) AS avg_age
FROM students
WHERE age > 18
GROUP BY department
HAVING AVG(age) > 20;
这里发生了这些事:
- 在
students表里选出age > 18的行(用WHERE)。 - 剩下的行按
department分组。 - 每组算出学生的平均年龄。
- 平均年龄小于等于 20 的组被
HAVING排除。 - 输出结果。
组合用法的例子
例子 1:分组前后都过滤
条件:找出学生人数大于 5 的院系,只算年龄大于 18 岁的学生。
原始表 students
| id | name | department | age | gpa |
|---|---|---|---|---|
| 1 | Alex Lin | ComputerSci | 20 | 3.8 |
| 2 | Maria Chi | Math | 22 | 3.5 |
| 3 | Anna Song | ComputerSci | 19 | 4.0 |
| 4 | Otto Art | Math | 17 | 3.9 |
| 5 | Liam Park | Physics | 21 | 3.7 |
| 6 | Jane Doe | ComputerSci | 23 | 3.6 |
| 7 | Tom Brown | Math | 25 | 3.4 |
| 8 | Sara White | Math | 19 | 3.8 |
| 9 | John Smith | ComputerSci | 20 | 3.7 |
| 10 | Emily Green | Physics | 18 | 3.9 |
| 11 | Mark Blue | ComputerSci | 21 | 3.5 |
| 12 | Zoe Black | Math | 22 | 3.6 |
| 13 | Max Gray | ComputerSci | 20 | 3.9 |
| 14 | Eva Gold | Math | 23 | 3.7 |
| 15 | Nick Silver | Physics | 19 | 3.8 |
查询:
SELECT department, COUNT(*) AS student_count
FROM students
WHERE age > 18
GROUP BY department
HAVING COUNT(*) > 5;
结果:-- 查询结果
| department | student_count |
|---|---|
| ComputerSci | 6 |
解释:
- 先删掉
age <= 18的行(WHERE条件)。 - 按院系分组(
GROUP BY department)。 - 算每组学生数量。
- 把学生数小于等于 5 的组去掉(
HAVING COUNT(*) > 5)。
例子 2:用 WHERE 代替 HAVING 的错误
条件:找出平均年龄大于 22 岁的院系。
错误的查询:
SELECT department, AVG(age) AS avg_age
FROM students
WHERE AVG(age) > 22
GROUP BY department;
错误:SQL 不让你在 WHERE 里用聚合函数 AVG,因为这时候聚合还没算出来。
正确的查询:
SELECT department, AVG(age) AS avg_age
FROM students
GROUP BY department
HAVING AVG(age) > 22;
这里 AVG(age) > 22 的条件是在分组后用的。
实用建议
如果你要过滤行,用 WHERE。例子:找出工资大于 5000 的所有员工。
SELECT *
FROM employees
WHERE salary > 5000;
如果你要过滤组,用 HAVING。例子:找出总工资大于 100000 的部门。
SELECT department, SUM(salary) AS total_salary
FROM employees
GROUP BY department
HAVING SUM(salary) > 100000;
复杂条件可以组合用 WHERE 和 HAVING。
例子:找出总人口超过一千万的国家,只算人口超过一百万的城市。
SELECT country, SUM(population) AS total_population
FROM cities
WHERE population > 1000000
GROUP BY country
HAVING SUM(population) > 10000000;
常见错误和解决办法
最常见的错误之一就是把 WHERE 和 HAVING 搞混。比如,试图在 WHERE 里用聚合函数:
SELECT department, COUNT(*)
FROM students
WHERE COUNT(*) > 10
GROUP BY department;
这种查询会报错,因为聚合还没算出来,WHERE 阶段用不了。正确做法是用 HAVING:
SELECT department, COUNT(*)
FROM students
GROUP BY department
HAVING COUNT(*) > 10;
还有一种错误是 WHERE 里用错条件。比如:
SELECT department, AVG(age) AS avg_age
FROM students
WHERE avg_age > 20
GROUP BY department;
这里 avg_age > 20 不对,因为 avg_age 还没算出来。解决办法——把条件放到 HAVING:
SELECT department, AVG(age) AS avg_age
FROM students
GROUP BY department
HAVING AVG(age) > 20;
希望你现在已经很清楚 WHERE 和 HAVING 的区别、怎么用、怎么避免常见错误。这些知识以后写复杂报表、分析数据、优化查询都超有用。也就是说,绝大多数你要写的 SQL 查询都离不开它们 :)
GO TO FULL VERSION