SQL JOIN 中 ON 与 WHERE 的过滤边界
解释 OUTER JOIN 中 ON 控制哪些右表行能够匹配,WHERE 控制联表后哪些结果行保留,以及条件放错位置为何会破坏 LEFT JOIN 语义。
#synthesis
#growing
#backend
#sql
#database
[!info] related notes
- 所属 MOC: database-moc
- 前置概念: sql-join
- 并列概念: sql-having-vs-where
- 相关概念: sql-group-by、sql-aggregate-functions
SQL JOIN 中 ON 与 WHERE 的过滤边界
范围
这篇只讨论 JOIN 场景里 ON 和 WHERE 的语义边界,尤其是 LEFT JOIN。
核心区别
ON
→ 哪些右表行有资格与左表匹配
WHERE
→ JOIN 完成后,哪些结果行还能保留
对 INNER JOIN,很多条件放在 ON 或 WHERE 最终可能得到相同结果;对 LEFT JOIN,二者经常不等价。
典型陷阱
需求:返回所有用户,以及每个人已发布文章的数量,包括 0 篇的用户。
条件放 WHERE
SELECT u.id, COUNT(a.id)
FROM users u
LEFT JOIN articles a
ON a.user_id = u.id
WHERE a.status = 'published'
GROUP BY u.id;
没有 published Article 的用户会得到:
user | a.status = NULL
NULL = 'published' 不为 TRUE,所以 WHERE 把整行删除,LEFT JOIN 的“保留左表”效果被破坏。
条件放 ON
SELECT u.id, COUNT(a.id)
FROM users u
LEFT JOIN articles a
ON a.user_id = u.id
AND a.status = 'published'
GROUP BY u.id;
此时 status 只决定哪些 Article 能匹配;匹配不到时 User 仍保留,a.id = NULL,所以 COUNT(a.id) = 0。
判断规则
问自己两个问题:
- 这是“关系匹配条件”吗?——倾向放
ON。 - 这是“最终结果必须满足的条件”吗?——倾向放
WHERE。
如果要求保留 OUTER JOIN 的未匹配行,对右表的过滤尤其要谨慎。
不要机械化
这不是“右表条件永远放 ON”。如果业务就是要求最终只保留满足右表条件的结果,那么 WHERE 完全合理。关键是先明确是否还需要保留未匹配左表行。
与 HAVING 的边界
ON:控制 JOIN 匹配。WHERE:过滤 JOIN 后、分组前的行。HAVING:过滤GROUP BY后的组。
三者解决的是不同阶段的问题。