full join不是找不匹配记录的首选方案,因其返回左右表全量并集但on条件仍会过滤不满足关联逻辑的组合,易误判“孤立行”;真正可靠的是left join...where right.key is null,它左表全量保留、右表无匹配时字段为null,再精准筛选null行,兼容主流数据库且性能稳定。

为什么FULL JOIN不是找不匹配记录的首选方案
直接用 FULL JOIN 查“不匹配记录”容易误判——它返回的是左右两表所有行的并集,但 ON 条件仍会过滤掉不满足关联逻辑的组合,导致你以为的“孤立行”其实被悄悄丢掉了。真正要找 A 表有、B 表没有,或 B 表有、A 表没有的记录,核心是识别 NULL 关键字段,而不是依赖 FULL JOIN 的“全量感”。
LEFT JOIN ... WHERE right.key IS NULL 才是查 A 有 B 无的标准写法
这是最可靠、可读性最强、数据库优化器也最友好的方式。关键点在于:左表全量保留,右表匹配失败时对应列全为 NULL,再用 WHERE 精准筛出这些 NULL 行。
常见错误包括:
- 写成
WHERE right.key = NULL——NULL不能用=判断,必须用IS NULL - 在
ON子句里加右表条件(如ON a.id = b.id AND b.status = 'active'),这会让本该被筛出的孤立行因条件提前过滤而消失 - 没给右表关联字段加索引,导致
JOIN扫描慢,尤其在百万级表上延迟明显
示例(查订单表有、但用户表缺失对应记录的订单):
SELECT o.* FROM orders o LEFT JOIN users u ON o.user_id = u.id WHERE u.id IS NULL;
需要同时查“两边孤立行”时,UNION ALL 比 FULL JOIN 更清晰可控
如果真要一次性拿到 A 有 B 无 + B 有 A 无 的全部记录,拼两个 LEFT JOIN 再 UNION ALL,比写一个带多重 IS NULL 判断的 FULL JOIN 更易读、更易调试、也更容易命中索引。
注意:
- 用
UNION ALL而非UNION,避免隐式去重开销(两边数据天然不重叠) - 统一字段顺序和类型,否则
UNION会报错;必要时用CAST或补NULL占位 - 别在
UNION外层再套复杂WHERE,会阻止下推优化
示例:
SELECT 'orders_only' AS source, o.id, o.user_id, NULL::int AS user_id_from_users FROM orders o LEFT JOIN users u ON o.user_id = u.id WHERE u.id IS NULL <p>UNION ALL</p><p>SELECT 'users_only' AS source, NULL::int AS id, NULL::int AS user_id, u.id FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE o.user_id IS NULL;</p>
MySQL 用户注意:FULL JOIN 本身就不支持
MySQL 直到 8.0.33 仍未实现 FULL JOIN 语法,写上去直接报错 ERROR 1064。此时若强行模拟,必须用 LEFT JOIN + RIGHT JOIN + UNION ALL 组合,和上面推荐的双 LEFT JOIN 写法实质一致。别被某些博客里“用 LEFT JOIN 加 RIGHT JOIN 模拟 FULL JOIN”的提法带偏——你真正需要的从来就不是 FULL JOIN,而是对 NULL 的明确捕获逻辑。
实际执行前,务必用 EXPLAIN 看一眼是否走了索引,特别是关联字段的索引是否存在、是否被用于 JOIN 条件而非仅 WHERE 过滤。










