LEFT JOIN 为什么漏掉左表记录?先看条件写在 ON 还是 WHERE

 2天前     29  

文章目录

想列出所有作者,并在旁边显示他们已发布的文章,我自然会想到 LEFT JOIN。可如果把“已发布”写进 WHERE,没有文章的人就不见了,只有草稿的人也不见了。问题不在连接类型写错,而在筛选发生的阶段。

我用 SQLite 3.53.1 做了一个内存实验:甲有一篇已发布文章,乙只有一篇草稿,丙没有文章。数据是专门构造的样本,不是线上故障记录。

先看容易写出的查询

CREATE TABLE authors(id INTEGER PRIMARY KEY, name TEXT);
CREATE TABLE articles(id INTEGER PRIMARY KEY, author_id INTEGER, status TEXT);
INSERT INTO authors VALUES (1,'甲'),(2,'乙'),(3,'丙');
INSERT INTO articles VALUES (10,1,'published'),(20,2,'draft');

SELECT a.name, b.id
FROM authors AS a
LEFT JOIN articles AS b ON b.author_id = a.id
WHERE b.status = 'published'
ORDER BY a.id;

实际结果只有一行:甲 | 10。乙的草稿被 WHERE 排除;丙补出来的右表字段都是 NULL,NULL = 'published' 也不会得到真,因此同样被排除。

SQLite 官方说明:外连接先完成 ON 匹配,再为未匹配的一侧补 NULL,之后才执行 WHERE;WHERE 只保留条件为真的行。[1] 这是解释查询结果的逻辑顺序,不代表数据库必须按这个顺序逐步执行物理操作。

要保留全部作者,把右表的匹配条件放进 ON

SELECT a.name, b.id
FROM authors AS a
LEFT JOIN articles AS b
  ON b.author_id = a.id
 AND b.status = 'published'
ORDER BY a.id;

这次实际得到三行:甲 | 10、乙 | NULL、丙 | NULL。我的目标是“保留全部作者,只匹配已发布文章”,所以这条才符合问题。

补一个 OR IS NULL,也不一定等价

我又试了在第一条查询的 WHERE 中改写为:

WHERE b.status = 'published' OR b.id IS NULL

结果是甲和丙,仍然没有乙。因为乙已经匹配到草稿,连接阶段不会再替他补一条空记录;草稿到 WHERE 阶段被删掉以后,数据库也不会回头再补一次。这是最容易漏掉的反例。

别把它记成“WHERE 不能写右表”

如果本来就只想找“至少有一篇已发布文章的作者”,排除乙和丙可能正是需求。左表自身的筛选,例如只看某类作者,也可以放在 WHERE。关键是先说清:条件是在限定“哪些记录能匹配”,还是限定“最终留下哪些行”。

我的检查办法是给查询补三种样本:能匹配、完全没匹配、匹配到了但不满足附加条件。只测第一种,很容易让一条错误的 LEFT JOIN 看起来正常。

本文为 AI 辅助整理的原创小型实验;2026年10月19日复核官方文档。结论限于文中的查询语义,不涉及性能比较。

一手来源

版权声明:leif 发表于 2天前,共 1292 字。
转载请注明:LEFT JOIN 为什么漏掉左表记录?先看条件写在 ON 还是 WHERE | 虎娃博客

暂无评论

暂无评论...