0Pricing
SQL Interview Prep · 课时

HAVING 与 WHERE

比较分组前后进行筛选的差异,并了解哪个子句可以看到汇总结果

HAVING 与 WHERE 是 CoddyKit 上的免费 SQL Interview Prep 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 SQL Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 SQL Interview Prep 课程共包含 4 节课。

您将遇到的问题

'WHERE 和 HAVING 有什么区别?' 是数据库面试中最常见的问题之一。较弱的回答是'HAVING 用于聚合'。有力的回答会解释每个子句何时执行,也就是查询流程中的执行时机。

这个时机就是全部关键:WHERE 在分组之前筛选行;HAVING 在聚合之后筛选分组。

它们在执行顺序中的位置

请回顾查询的逻辑执行顺序:

  • FROM / JOIN → 构建行集合
  • WHERE → 筛选单独的行
  • GROUP BY → 将行合并为分组
  • HAVING → 筛选分组
  • SELECT → 投影列
  • ORDER BY → 排序

WHERE 执行时分组还不存在;HAVING 在分组之后执行,因此 HAVING 可以读取聚合值,而 WHERE 不行。

WHERE 无法读取聚合值

由于 WHERE 在分组之前执行,此时还没有聚合值。 在任何标准数据库中,写出 WHERE COUNT(*) > 5 都会产生语法错误。

面试官经常故意放入这一行,以检查您是否理解查询流程。执行 WHERE 时,聚合值还不存在。

-- ERROR: aggregate not allowed in WHERE
SELECT department, COUNT(*)
FROM employees
WHERE COUNT(*) > 5
GROUP BY department;

HAVING 筛选分组

将聚合条件移到 HAVING 中就可以正常工作,因为 HAVING 在分组及其聚合值计算完成后执行。

可以将其理解为:'先按员工分组,然后只保留人数超过五人的部门。'

SELECT department, COUNT(*) AS headcount
FROM employees
GROUP BY department
HAVING COUNT(*) > 5;

将行筛选条件放入 WHERE

另一种常见错误是在 HAVING 中筛选原始行。这样通常也能得到正确答案,但会更慢且容易误导,因为您对本来打算丢弃的行进行了分组。

经验法则:根据原始列值筛选 → 使用 WHERE;根据聚合值筛选 → 使用 HAVING。尽早筛选行可以减少分组需要处理的数据量。

-- Better: drop inactive rows BEFORE grouping
SELECT department, COUNT(*) AS headcount
FROM employees
WHERE status = 'active'
GROUP BY department
HAVING COUNT(*) > 5;

同时使用两个子句

完整的查询通常会同时使用两者。WHERE 先缩小行的范围;HAVING 再保留符合条件的分组。按照从上到下的顺序阅读,正好对应逻辑执行顺序。

示例:在今年下单的订单中,找出消费总额超过 1000 的客户。

SELECT customer_id, SUM(amount) AS total_spent
FROM orders
WHERE order_date >= '2024-01-01'
GROUP BY customer_id
HAVING SUM(amount) > 1000;

对非聚合列使用 HAVING

HAVING 可以引用分组列,而不仅仅是聚合值。HAVING department = 'Sales' 合法,但没有必要:该筛选条件应放入 WHERE,以便更早执行。

如果面试官给出一个用于筛选普通分组列的 HAVING,您应指出:'为了提高效率,请将它移到 WHERE。'

-- Works but inefficient; prefer WHERE department = 'Sales'
SELECT department, COUNT(*)
FROM employees
GROUP BY department
HAVING department = 'Sales';

不使用 GROUP BY 的 HAVING

有一个容易忽略的情况:即使不使用 GROUP BY,HAVING 也合法。整个表会成为一个隐式分组,而 HAVING 会筛选这个唯一的分组。

如果聚合条件为假,结果会有零行;如果为真,则会有一行。这种写法很少有实际用途,但面试官会借此确认您是否理解隐式分组的概念。

-- Returns the count only if the table has > 100 rows
SELECT COUNT(*) AS total
FROM orders
HAVING COUNT(*) > 100;

HAVING 可以使用 SELECT 别名吗

和其他地方的别名作用域问题一样,不同数据库方言的行为也不同。Postgres 和 MySQL 允许 HAVING 引用 SELECT 别名;SQL Server 和 Oracle 则不允许。

更具可移植性的做法是在 HAVING 中重复聚合表达式。这样在所有数据库引擎中都能工作,也能避免跨数据库面试中的意外情况。

-- Portable: repeat the aggregate, do not rely on the alias
SELECT region, SUM(amount) AS total
FROM sales
GROUP BY region
HAVING SUM(amount) > 5000;

从性能角度理解

如果想让回答更有说服力,请将子句与性能联系起来:WHERE 会减少分组引擎必须扫描的行数,并且可以使用索引;HAVING 作用于已经聚合的分组,因此无法降低分组本身的成本。

面试官希望您得出的结论是:尽可能早地应用每个筛选条件。只有确实依赖聚合值的条件才需要使用 HAVING。

一句话回答

面试时请记住:'WHERE 在分组之前筛选行,无法读取聚合值;HAVING 在聚合之后筛选分组,并且是唯一可以检验聚合值的子句。'

接着说出执行顺序,您就给出了一个完整且听起来像资深开发者的回答。

快速检查

请判断每个条件应属于哪个子句。

回顾

WHERE:在 GROUP BY 之前筛选行,不允许使用聚合值。HAVING:在聚合之后筛选分组,这是唯一允许使用聚合条件的位置。

  • 将原始列筛选条件放入 WHERE,以提高速度并利用索引。
  • HAVING 可以引用分组列,但普通筛选不应这样做。
  • 不使用 GROUP BY 时,HAVING 会作用于整个表形成的隐式分组。
  • 在 HAVING 中重复聚合表达式,以确保跨数据库方言的兼容性。

常见问题解答

「HAVING 与 WHERE」课时是免费的吗?

是的 — 「HAVING 与 WHERE」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 SQL Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 SQL Interview Prep 课程共包含 4 节课。

「HAVING 与 WHERE」这节课中我会学到什么?

比较分组前后进行筛选的差异,并了解哪个子句可以看到汇总结果 你通过在浏览器中直接运行的动手代码来练习 SQL Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 SQL Interview Prep 需要有经验吗?

无需任何先前经验。CoddyKit 上的 SQL Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「HAVING 与 WHERE」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 SQL Interview Prep 课中编写并运行代码吗?

能。每节 SQL Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. SELECT 列的 GROUP BY 规则
  2. HAVING 与 WHERE
  3. 按多列和表达式分组
  4. 统计并筛选分组
← 返回 SQL Interview Prep