SQL HAVING 和 WHERE 的区别

WHERE 过滤原始行,HAVING 过滤分组结果;两者的核心区别在于生效阶段不同。

#type / synthesis #status / growing #tech / dev / backend #resource / sql #resource / database

[!info] related notes

SQL HAVING 和 WHERE 的区别

一句话结论

WHERE 过滤的是参与分组前的原始行,HAVING 过滤的是 GROUP BY 之后的分组结果。

为什么这两个总被一起问

因为它们看起来都像“加条件”,但语义层级完全不同:

  • WHERE 决定哪些行能进入后续计算
  • HAVING 决定哪些组能出现在最终结果里

面试里很多人语法能背出来,但一写到聚合查询就会把条件放错位置。

从查询流程理解

可以先用一个粗略心智模型理解:

  1. FROM / JOIN
  2. WHERE
  3. GROUP BY
  4. 聚合计算
  5. HAVING
  6. SELECT
  7. ORDER BY
  8. LIMIT

这里最关键的是:

  • WHERE 在分组前
  • HAVING 在聚合后

所以:

  • WHERE 适合筛原始数据
  • HAVING 适合筛统计结果

最小例子

例 1:先筛行,再分组

统计 status = 'paid' 的订单里,每个用户有多少单:

SELECT user_id, COUNT(*) AS order_count
FROM orders
WHERE status = 'paid'
GROUP BY user_id;

这里 WHERE 的意思是:

  • 先把未支付订单排除
  • 再只对支付成功的订单做分组统计

例 2:先分组,再筛组

查出订单数大于 3 的用户:

SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
HAVING COUNT(*) > 3;

这里 HAVING 的意思是:

  • 先按用户分组
  • 再把订单数不超过 3 的组过滤掉

例 3:两者一起出现

查出支付成功订单中,下单数大于 3 的用户:

SELECT user_id, COUNT(*) AS order_count
FROM orders
WHERE status = 'paid'
GROUP BY user_id
HAVING COUNT(*) > 3;

这题里两层条件分别回答两个问题:

  • WHERE:哪些行参与统计
  • HAVING:哪些统计结果保留

最容易错的地方

1. 把聚合条件写进 WHERE

下面这种写法通常是错的:

SELECT user_id, COUNT(*) AS order_count
FROM orders
WHERE COUNT(*) > 3
GROUP BY user_id;

原因是执行 WHERE 时,分组和聚合结果还没出来。

2. 能用 WHERE 的条件却全塞给 HAVING

例如:

SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
HAVING status = 'paid';

这类写法语义就不稳。status = 'paid' 这种行级过滤应尽量放在 WHERE,这样:

  • 语义更清楚
  • 参与分组的数据更少
  • 执行代价通常也更合理

3. 以为 HAVING 只能和 GROUP BY 一起出现

多数面试场景里它确实和 GROUP BY 一起讨论,但本质上 HAVING 是对聚合结果加条件。只是如果没有分组,这个场景在业务里没那么常见。

工程上该怎么选

可以直接用一句话判断:

  • 条件针对原始行,用 WHERE
  • 条件针对聚合结果,用 HAVING

如果一个条件本来就能在分组前过滤,优先放到 WHERE,不要无脑推迟到 HAVING

最短记忆方式

  • WHERE:先筛行
  • GROUP BY:再分组
  • HAVING:最后筛组
创建于 2026/5/7 更新于 2026/5/27