HAVING 和 WHERE 的执行顺序:为什么不能在 WHERE 中用聚合函数?
开门见山:聚合函数用于在 GROUP BY 之后,对分组后的整体数据进行计算。
WHERE 执行时,聚合函数所需的分组数据尚未计算,因此无法直接在 WHERE 中使用聚合函数。
一、SQL执行顺序
书写顺序
SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY
实际执行顺序
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
二、WHERE 过滤行
WHERE 用于过滤行(在分组前过滤数据)
例1:595. 大的国家
SELECT
w.name, w.population, w.area
FROM
World AS w
WHERE
w.area >= 3000000
OR
w.population >= 25000000
;
例2:620. 有趣的电影
SELECT
id, movie, description, rating
FROM
cinema
WHERE
MOD(id, 2) = 1
AND
description != 'boring'
ORDER BY
rating DESC
;
例3:1757. 可回收且低脂的产品
SELECT
p.product_id
FROM
Products AS p
WHERE
p.low_fats = 'Y'
AND
p.recyclable = 'Y'
;
三、HAVING 过滤分组
HAVING 用于过滤分组(在分组后过滤数据,通常与 GROUP BY 搭配)
例1:182. 查找重复的电子邮箱
SELECT
email AS Email
FROM
Person
GROUP BY
email
HAVING
COUNT(*) >= 2
;
例2:596. 超过 5 名学生的课
SELECT
class
FROM
Courses
GROUP BY
class
HAVING
COUNT(*) >= 5
;
例3:1050. 合作过至少三次的演员和导演
SELECT
actor_id, director_id
FROM
ActorDirector
GROUP BY
actor_id, director_id
HAVING
COUNT(*) >= 3
;
四、为什么不能在 WHERE 中用聚合函数?
| 对比项 | WHERE | HAVING |
|---|---|---|
| 执行时机 | 分组前 | 分组后 |
| 过滤对象 | 原始表中的行 | 分组后的结果集 |
| 聚合函数 | 不能 | 可以 |
| 常见搭配 | 独立使用,无需 GROUP BY | 与 GROUP BY 一起使用(SQL 标准) |
聚合函数用于在 GROUP BY 之后,对分组后的整体数据进行计算。
WHERE 执行时,聚合函数所需的分组数据尚未计算,因此无法直接在 WHERE 中使用聚合函数。
五、特殊情况
WHERE 中间接使用聚合函数
WHERE子句本身不能直接使用聚合函数,但可以通过嵌套子查询,在子查询内部使用聚合函数。此时WHERE作用的是子查询的结果(已聚合),而非原始表的聚合,本质上仍符合执行顺序规则。
例: 184. 部门工资最高的员工
SELECT
d.name AS Department,
e.name AS Employee,
e.salary AS Salary
FROM
Employee e
JOIN
Department d
ON
e.departmentId = d.id
WHERE
e.salary = (
SELECT
MAX(salary)
FROM
Employee
WHERE
departmentId = e.departmentId
);
不存在有效分组时,HAVING 与 WHERE 等效
1. 没有分组
当查询中没有分组时,HAVING 的作用等同于 WHERE,因为此时不存在“分组后”的概念。
例如二中的例1也可以这么写,但不推荐:
SELECT
w.name, w.population, w.area
FROM
World AS w
HAVING
w.area >= 3000000
OR
w.population >= 25000000
;
2. 分组内结果恰好只有一行
如果 dept_id = 1 刚好只有1名员工,则结果是相同的(此时虽然分组了,但相当于没分)。
但是结果有多少行,在运行前我们是不知道的。
因此如果要筛选 dept_id = 1 的员工,应该使用 WHERE 的写法。
-- (假设dept_id = 1的员工恰好只有1个)
SELECT
dept_id
FROM
employees
GROUP BY
dept_id
HAVING
dept_id = 1
;
-- 等价于:
SELECT
dept_id
FROM
employees
WHERE
dept_id = 1 -- 直接过滤部门 ID
;
参考资料
[1] 一文讲懂SQL语法顺序与执行顺序
浙公网安备 33010602011771号