SQL 面试题:WHERE 和 HAVING、ON 有什么区别?
WHERE、HAVING、ON 都能写条件,但它们的作用阶段和作用对象不同。
核心区别
| 关键字 | 作用对象 | 执行阶段 | 能否使用聚合函数 |
|---|---|---|---|
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;
执行逻辑是:
- 先通过
WHERE找出已支付订单。 - 再按照
user_id分组。 - 最后计算每个用户的总金额。
下面这种写法通常不合法:
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.status 是 NULL,无法满足条件,因此会被过滤掉。此时效果接近 INNER JOIN。
面试总结
可以这样回答:
ON用于定义表之间的连接和匹配条件;WHERE在分组前过滤原始数据行;HAVING在分组和聚合之后过滤分组结果。
对于外连接,条件写在ON还是WHERE中会影响是否保留未匹配的数据,这是三者中最常见的面试考点。
SQL 常见逻辑执行顺序可以记为:
FROM / JOIN
→ ON
→ WHERE
→ GROUP BY
→ HAVING
→ SELECT
→ ORDER BY
→ LIMIT
浙公网安备 33010602011771号