← 返回题目列表

SQL 中 WHERE 和 HAVING 有什么区别?

高频 简单 第 3 / 28 题 更新于 2026/07/27
SQLWHEREHAVINGGROUP BY

简化版

WHERE 在分组前过滤行,HAVING 在分组后过滤组,因此聚合条件通常写在 HAVING。能在分组前确定的普通条件应尽量写进 WHERE,既准确表达语义,也能减少参与聚合的数据。

详细版

SELECT department_id, COUNT(*) AS employee_count
FROM employee
WHERE status = 'active'
GROUP BY department_id
HAVING COUNT(*) >= 10;

这里 WHERE 先排除非在职员工,GROUP BY 再按部门分组,HAVING 最后只保留在职员工不少于 10 人的部门。

WHERE COUNT(*) >= 10 是错误思路,因为过滤行时聚合结果尚未形成。反过来,把 status = 'active' 只写在 HAVING 中不仅可能不符合严格分组规则,还会让无关行先参与分组。

完整版教学

一、两者过滤的对象不同

WHERE 面对的是 FROM/JOIN 产生的单行数据;HAVING 面对的是 GROUP BY 形成的组。可以把查询理解为先挑原材料,再按规则装箱,最后筛选箱子。

因此以下职责很清楚:

  • 时间、状态、地区等行级条件放 WHERE
  • 每组数量、总额、平均值等聚合条件放 HAVING
  • 同时存在时,两者可以共同出现。

二、为什么聚合函数不能直接放 WHERE

SELECT customer_id, SUM(amount)
FROM orders
WHERE SUM(amount) > 10000
GROUP BY customer_id;

处理 WHERE 时还没有分组,也就没有每个客户的 SUM(amount)。正确写法是:

SELECT customer_id, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
HAVING SUM(amount) > 10000;

部分数据库允许 HAVING 使用聚合别名,部分场景需要重复聚合表达式。若追求跨数据库可移植性,应了解目标数据库的别名规则。

三、没有 GROUP BY 能不能使用 HAVING

可以。没有显式 GROUP BY 且查询包含聚合时,所有输入行可视为一个组:

SELECT COUNT(*) AS total
FROM orders
HAVING COUNT(*) > 0;

它要么返回一行,要么不返回。不过普通行级过滤仍应使用 WHERE,不要因为语法允许就混淆职责。

四、性能优化的关键是尽早缩小数据集

SELECT customer_id, COUNT(*)
FROM orders
WHERE created_at >= '2026-01-01'
GROUP BY customer_id
HAVING COUNT(*) >= 5;

日期条件在聚合前即可判断,写入 WHERE 后,旧订单无需进入分组。优化器有时能自动做谓词下推,但开发者仍应写出正确、清晰的语义,且聚合条件通常无法下推到分组前。

五、常见错误

  • WHERE 过滤聚合函数,忽略了处理阶段;
  • 把所有条件都堆到 HAVING,增加计算量并降低可读性;
  • HAVING 中引用既未分组也未聚合的列,结果依赖宽松模式或直接报错;
  • 以为 HAVING 必须与 GROUP BY 同时出现。

六、常见误区与追问

条件类型推荐位置原因
单行状态、时间、租户WHERE聚合前过滤,减少输入行
SUMCOUNT 等聚合结果HAVING聚合后才有组级值
可提前判断的维度条件WHERE语义清楚且利于优化
组级阈值条件HAVING过滤分组结果
  • 误区:HAVING 是 WHERE 的高级版。 二者过滤阶段不同,WHERE 过滤原始行,HAVING 过滤分组聚合后的结果。
  • 误区:聚合函数可以直接写在 WHERE。 WHERE 发生在聚合之前,此时还没有 SUM(amount)COUNT(*) 这类组级结果。
  • 误区:所有条件都放 HAVING 也没问题。 虽然某些数据库能优化下推,但行级条件写在 WHERE 更清晰,也更有机会提前减少扫描和分组成本。
  • 追问:没有 GROUP BY 能不能写 HAVING? 可以,整个结果被看作一个组;常见于筛选总计结果,但不同数据库对非聚合列限制可能不同。
  • 追问:别名能不能在 HAVING 中使用? 这取决于数据库实现;为了可移植性,面试中更稳妥的回答是直接写聚合表达式或包一层子查询。
  • 追问:优化聚合查询的第一步是什么? 先把状态、时间范围、租户等行级条件放到 WHERE,再让 GROUP BYHAVING 处理更小的数据集。

记忆钩子:WHERE 先拦住不该进组的行,HAVING 再筛掉聚合后不合格的组。

七、加强记忆

WHERE 先筛行,GROUP BY 再成组,HAVING 后筛组。看到“状态、日期、单价”等单行条件先想 WHERE,看到“数量、总和、平均值”等聚合结果先想 HAVING;能够提前过滤的数据不要留到分组以后。