left join + is null是最可靠anti join写法,因语义清晰、兼容性强且优化器易识别;必须用右表关联字段is null判断,且该字段需为not null或业务上可明确区分null含义。

用 LEFT JOIN + IS NULL 实现 Anti Join 最可靠
SQL 标准里没有 ANTI JOIN 关键字,但所有主流数据库(PostgreSQL、MySQL 8.0+、SQL Server、Oracle)都支持用 LEFT JOIN 配合 WHERE ... IS NULL 模拟。这是最直观、可读性最强、执行计划也最容易被优化器识别的方式。
常见错误是把过滤条件写在 ON 子句里却忘了加 IS NULL,结果返回的是左表全量——因为 LEFT JOIN 本就会保留左表所有行,不加 WHERE 就没“排除”效果。
- 正确写法:
SELECT a.* FROM table_a a LEFT JOIN table_b b ON a.id = b.a_id WHERE b.a_id IS NULL - 错误写法:
SELECT a.* FROM table_a a LEFT JOIN table_b b ON a.id = b.a_id AND b.a_id IS NULL(这会让b始终为 NULL,但语义混乱且可能被误优化) - 注意:必须检查
b表中用于关联的列是否允许 NULL;如果b.a_id是NOT NULL字段,用它判IS NULL才安全
EXISTS 和 NOT EXISTS 更适合大表驱动小表场景
当左表极大、右表较小时,NOT EXISTS 往往比 LEFT JOIN 更高效——它能利用右表索引快速“否定”,而不需要构造中间连接结果集。
典型误用是把 NOT IN 当作替代方案,但只要 table_b.id 中存在任意一个 NULL,整个 NOT IN 表达式就返回空结果集(三值逻辑陷阱),这点极易被忽略。
- 推荐:
SELECT * FROM table_a a WHERE NOT EXISTS (SELECT 1 FROM table_b b WHERE b.a_id = a.id) - 避免:
SELECT * FROM table_a WHERE id NOT IN (SELECT a_id FROM table_b)(除非你能 100% 确保子查询结果无NULL) -
NOT EXISTS对NULL安全,且多数引擎会自动将它转为半连接(semi-join)优化
不同数据库对 USING 的处理会影响 Anti Join 正确性
用 USING(col) 替代 ON a.col = b.col 看似简洁,但在 Anti Join 场景下容易出错:某些数据库(如 PostgreSQL)在 USING 后,WHERE b.col IS NULL 会报列不存在错误,因为 USING 会合并同名列,只保留一个可见的 col。
更隐蔽的问题是,如果两张表都有 col 列但类型隐式转换规则不同(比如 MySQL 中 VARCHAR 和 INT),USING 可能触发意外的类型转换,导致本该匹配的行被漏掉。
- 稳妥做法:一律用显式
ON+ 别名限定,例如ON a.id = b.a_id - 若坚持用
USING,判空时得用WHERE b.a_id IS NULL而不是WHERE a_id IS NULL(后者在部分方言中不可用) - 测试时务必查执行计划,确认
NULL判定发生在右表别名上
UNION ALL + GROUP BY 方案只在特殊聚合需求下才值得考虑
有人用 UNION ALL 把两张表字段对齐后 GROUP BY + HAVING COUNT(*) = 1 来找差异,这种写法性能差、可读性低,仅适用于需要同时获取“左有右无”和“右有左无”两类记录的对称差集场景。
它的致命缺陷是无法保留原始表中的非关联字段(比如 table_a.name 和 table_b.description 类型不兼容,强行 UNION 会报错或截断),而且 GROUP BY 开销远高于单边扫描。
- 仅当明确需要「对称差集」且两张表结构高度一致时,才考虑:
(SELECT id, 'a' as src FROM table_a) UNION ALL (SELECT a_id, 'b' FROM table_b) GROUP BY id HAVING COUNT(*) = 1 - 日常 Anti Join 场景下,这个方案纯属绕路,99% 的情况应该直接放弃
真正容易被忽略的是关联字段的空值处理和索引覆盖:如果 table_b.a_id 没有索引,NOT EXISTS 和 LEFT JOIN 都会变慢;如果该字段允许 NULL,又没在业务逻辑中明确定义“NULL 代表什么”,Anti Join 结果可能不符合预期——这些不在语法层面,却决定最终数据是否可信。











