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语法顺序与执行顺序

[2] MySQL查询执行顺序:一张图看懂SQL是如何工作的

posted @ 2025-07-14 11:20  苦涩如影相随固  阅读(56)  评论(0)    收藏  举报