您好!欢迎访问家园网-www.jy.wang!

家园网

HAVING能不能换成WHERE呢?

网络 作者:本站 点击:
SELECT cat_id, shop_price, COUNT(cat_id) AS 个数
FROM goods
WHERE (store_count < 1000)
GROUP BY cat_id,shop_price
HAVING (shop_price >
          (SELECT AVG(shop_price)
          FROM goods))
ORDER BY shop_price DESC;

那上面的sql代码中,HAVING能不能换成WHERE呢?用两个WHERE不就得了?


这是一个非常好的问题,直接触及了 SQL 核心的执行逻辑。

结论是:不能替换,两个 WHERE 也做不到。

下面我从词源角度(延续你之前的兴趣)执行机制角度来解释为什么。


一、词源角度的直观理解

回顾我们刚才说的:

  • WHERE(在哪里):在数据还在原始位置(行)时,逐一检查。就像在仓库里逐个检查商品:“这个商品库存少于 1000 吗?”

  • HAVING(拥有):在数据已经被装进组里之后,检查这个组“是否拥有某种属性”。就像把商品按类别和价格堆成一堆一堆之后,再判断:“这一堆的平均价格,是否大于全仓库的平均价?”

逻辑上

“仓库里库存 < 1000 的商品” → WHERE
“分组后,该组的单价 > 全表均价” → HAVING

这两个检查发生在完全不同的时间点,不能混在一起。


二、为什么不能用两个 WHERE?

假设你写成:

sql

SELECT cat_id, shop_price, COUNT(cat_id)FROM goodsWHERE store_count < 1000
  AND shop_price > (SELECT AVG(shop_price) FROM goods)GROUP BY cat_id, shop_price

问题出在这里:

  1. 第二个 WHERE 中的子查询 (SELECT AVG(shop_price) FROM goods)

    • 计算的是全表所有商品的平均价格(包括那些 store_count >= 1000 的商品)。

    • 而你真正想要的逻辑是:

      已经过滤掉库存≥1000的商品之后,再计算剩余商品的均价,然后用这个均价去比较每个分组。

  2. 语义错误
    你的原意(从 HAVING 推测)是:

    如果用 WHERE 写子查询,子查询无法“看到”外层 WHERE 已经过滤后的结果(除非用相关子查询且位置放对,但这里放不对)。

    • 先筛出 store_count < 1000 的商品

    • 对这些商品按 cat_id, shop_price 分组

    • 计算这些被筛过的商品的均价

    • 只保留那些 shop_price > 这个均价 的分组


三、用两个 WHERE 为什么也不行?

你可能会想:

sql

-- 第一个 WHERE 过滤 store_count-- 第二个 WHERE 过滤 shop_price > 子查询-- 但子查询必须基于第一个 WHERE 之后的数据

实际上 SQL 不允许这样写,因为:

  • WHERE 执行时,分组尚未发生

  • 子查询中的 AVG(shop_price) 如果放在 WHERE 里,它只能计算全表或明确指定的范围,无法自动引用“外层 WHERE 已经过滤后的数据集”,除非写成相关子查询且放在 HAVING 里(那就回到原点了)。


四、执行顺序才是根本原因(关键点)

SQL 的逻辑执行顺序(不是书写顺序)是:

  1. FROM → 确定数据来源

  2. WHERE → 筛选行(此时没有分组,没有聚合

  3. GROUP BY → 分组

  4. HAVING → 筛选分组(此时可以用聚合函数

  5. SELECT → 选择列

  6. ORDER BY → 排序

你的 HAVING 里用了 shop_price > (SELECT AVG(shop_price) FROM goods)
这个 AVG(shop_price) 是在 HAVING 阶段计算的,而 HAVING 阶段的数据已经是 WHERE 之后、GROUP BY 之后 的数据集。
子查询中的 FROM goods 如果不加额外条件,确实会看到全表,但这里你恰好需要的是全表均价(未过滤 store_count),所以巧合地没问题。
但如果你想要的是“过滤后的均价”,就必须把子查询写成相关形式并放在 HAVING 中。


五、能不能改成只用 WHERE?

不能,因为:

  • AVG(shop_price) 是聚合函数

  • 聚合函数的结果无法在 WHERE 中使用(WHERE 执行时分组还未形成,聚合值不存在)

唯一的例外是用窗口函数(但那是另一种写法,不是“两个 WHERE”)。


六、一句话总结(词源 + 逻辑)

WHERE 问的是“这一行在哪里(满足什么行级条件)”,HAVING 问的是“这一组拥有什么(聚合特征)”。
你无法在行筛选阶段问一个组级问题,就像不能在拆包裹时问“这一堆包裹平均重量是多少”——必须先把包裹分组堆好才能问。

如果你愿意,我可以把这个查询改成正确且更高效的写法(比如用窗口函数或 CTE),让你直观对比 HAVING 的不可替代性



标签: