SQL 面试题:WHERE 和 HAVING、ON 有什么区别?

WHEREHAVINGON 都能写条件,但它们的作用阶段和作用对象不同

核心区别

关键字 作用对象 执行阶段 能否使用聚合函数
ON 两张表连接时的匹配条件 JOIN 阶段 一般不用于聚合函数
WHERE 原始行数据 分组前 通常不能直接使用聚合函数
HAVING 分组后的结果 GROUP BY 可以使用聚合函数

1. WHERE:分组前过滤数据

例如查询有效订单:

SELECT user_id, SUM(amount) AS total_amount
FROM orders
WHERE status = 'paid'
GROUP BY user_id;

执行逻辑是:

  1. 先通过 WHERE 找出已支付订单。
  2. 再按照 user_id 分组。
  3. 最后计算每个用户的总金额。

下面这种写法通常不合法:

SELECT user_id, SUM(amount)
FROM orders
WHERE SUM(amount) > 1000
GROUP BY user_id;

因为执行 WHERE 时,SUM(amount) 还没有计算出来。


2. HAVING:分组后过滤结果

查询已支付订单总额大于 1000 的用户:

SELECT user_id, SUM(amount) AS total_amount
FROM orders
WHERE status = 'paid'
GROUP BY user_id
HAVING SUM(amount) > 1000;

这里:

  • WHERE status = 'paid':过滤订单明细。
  • HAVING SUM(amount) > 1000:过滤分组统计结果。

可以简单记为:

WHERE 过滤行,HAVING 过滤组。

虽然部分数据库允许 HAVING 不配合 GROUP BY 使用,但普通字段条件优先写在 WHERE 中,通常性能更好、语义也更清晰。


3. ON:控制表连接时如何匹配

例如查询用户和订单:

SELECT u.id, u.name, o.amount
FROM users u
JOIN orders o
  ON u.id = o.user_id;

ON u.id = o.user_id 表示两张表按照什么规则连接。

也可以在 ON 中增加条件:

SELECT u.id, u.name, o.amount
FROM users u
LEFT JOIN orders o
  ON u.id = o.user_id
 AND o.status = 'paid';

含义是:

  • 保留所有用户。
  • 只连接状态为 paid 的订单。
  • 没有已支付订单的用户仍然会出现,订单字段为 NULL

ON 和 WHERE 最容易考的区别

对于 INNER JOIN,下面两种写法通常结果相同:

SELECT *
FROM users u
INNER JOIN orders o
  ON u.id = o.user_id
 AND o.status = 'paid';
SELECT *
FROM users u
INNER JOIN orders o
  ON u.id = o.user_id
WHERE o.status = 'paid';

但对于 LEFT JOIN,结果可能完全不同。

条件写在 ON 中

SELECT *
FROM users u
LEFT JOIN orders o
  ON u.id = o.user_id
 AND o.status = 'paid';

所有用户都会保留,只匹配已支付订单。

条件写在 WHERE 中

SELECT *
FROM users u
LEFT JOIN orders o
  ON u.id = o.user_id
WHERE o.status = 'paid';

没有订单的用户,其 o.statusNULL,无法满足条件,因此会被过滤掉。此时效果接近 INNER JOIN

面试总结

可以这样回答:

ON 用于定义表之间的连接和匹配条件;WHERE 在分组前过滤原始数据行;HAVING 在分组和聚合之后过滤分组结果。
对于外连接,条件写在 ON 还是 WHERE 中会影响是否保留未匹配的数据,这是三者中最常见的面试考点。

SQL 常见逻辑执行顺序可以记为:

FROM / JOIN
→ ON
→ WHERE
→ GROUP BY
→ HAVING
→ SELECT
→ ORDER BY
→ LIMIT
posted @ 2026-07-27 16:07  余风0903  阅读(1)  评论(0)    收藏  举报