导读:本期聚焦于小伙伴创作的《SQL如何查询出只存在于A表而不存在于B表的数据?利用LEFT JOIN与IS NULL过滤》,敬请观看详情。想从订单表里捞出那些没有对应发货记录的订单,直接用内连接只会留下两边匹配的行,反而把目标数据漏掉了。LEFT JOIN配合IS NULL是关系型数据库里最直白的差集实现方式:以A表为基准左连B表,匹配不上的B表字段会变成空值,再用WHERE条件把空值行筛出来就是A有B无的结果。这种方式不依赖子查询,执行计划通常能走索引,在MySQL、PostgreSQL里都稳定可用。掌握它之后,还能延伸到NOT EXISTS写法和性能差异,面对千万级数据也能选对方案。

在关系型数据库的日常查询中,我们经常会遇到这样的需求:找出在A表中存在、但在B表中没有对应记录的数据行。这类问题本质上就是求两个集合的差集。利用LEFT JOIN配合IS NULL是最经典且兼容性最好的实现方式,下面我们详细拆解它的原理与写法。

SQL如何查询出只存在于A表而不存在于B表的数据?利用LEFT JOIN与IS NULL过滤

一、LEFT JOIN与IS NULL的基本原理

LEFT JOIN(左连接)会以左表也就是A表为基准,将B表中满足连接条件的记录拼接到结果里。如果B表中找不到匹配的行,那么B表的所有选中列都会以NULL填充。基于这个特性,只要我们在连接之后,用WHERE子句过滤出B表连接键为NULL的记录,就能精准得到那些只存在于A表、不存在于B表的数据。

这种做法之所以被广泛使用,是因为它语义清晰,不需要写复杂的子查询,而且大多数数据库的优化器都能针对LEFT JOIN生成高效的执行计划。相比之下,使用NOT IN如果遇到NULL值还容易产生意料之外的结果,而LEFT JOIN加IS NULL则没有这个隐患。

二、基础代码示例

假设我们有两张表:orders表存放订单信息,shipments表存放发货信息,订单通过order_id关联。现在我们要查出所有未发货的订单。

-- 查询只存在于orders表但不存在于shipments表的订单
SELECT o.order_id, o.create_time, o.amount
FROM orders o
LEFT JOIN shipments s
  ON o.order_id = s.order_id
WHERE s.order_id IS NULL;

在上面这段代码中,orders作为左表,shipments作为右表。连接条件是两个表的order_id相等。对于那些没有发货记录的订单,右表shipmentsorder_id自然是NULL,于是被WHERE条件筛选出来。

需要特别注意的是,WHERE过滤的必须是右表的非主键或连接键字段为NULL,通常使用连接用的字段即可。如果误把过滤条件写进ON子句,就会改变连接行为,导致结果不正确。

三、与NOT EXISTS写法对比

除了LEFT JOIN加IS NULL,开发者也常使用NOT EXISTS来实现相同逻辑。两者在结果上一致,但在执行机制和适用场景上有细微差别。

-- 使用NOT EXISTS实现同样需求
SELECT o.order_id, o.create_time, o.amount
FROM orders o
WHERE NOT EXISTS (
  SELECT 1
  FROM shipments s
  WHERE s.order_id = o.order_id
);

NOT EXISTS在语义上更直接表达“不存在”的含义,某些数据库在右表字段有索引时,会对NOT EXISTS做半连接优化,效率极高。而LEFT JOIN加IS NULL在表结构复杂、需要同时取出左表多列时,书写起来更直观。从可读性角度,LEFT JOIN方式对新手更友好,因为整个查询是一个扁平的语句。

在大数据量下,如果shipments.order_id上有索引,两种写法性能通常接近;但若B表极小,LEFT JOIN可能先扫小表再哈希匹配,而NOT EXISTS可能走嵌套循环。实际项目中建议用EXPLAIN查看执行计划再做选择。

四、常见误区与避坑

一个常见错误是在LEFT JOIN之后,把右表的条件写进WHERE却忘了处理NULL,或者在ON里写了本应属于WHERE的过滤,导致左表记录被提前丢掉。例如下面这种错误示范:

-- 错误示例:在ON里加右表额外条件,可能过滤掉左表基准行
SELECT o.order_id
FROM orders o
LEFT JOIN shipments s
  ON o.order_id = s.order_id
  AND s.status = 'done'
WHERE s.order_id IS NULL;

上述代码虽然也能跑,但s.status = 'done'放在ON里意味着只有已发货的才参与连接,未发货的B表侧为NULL,于是连“已发货但想查未发货”的逻辑都乱了。正确做法是,如果要把B表某状态作为“存在”的依据,应把该条件放入ON,但明确业务含义;若只是查完全无记录,则ON只写关联键。

另一个坑是字段类型不一致,比如A表order_id是字符串,B表是数字,某些数据库会做隐式转换导致索引失效。保证关联字段类型一致,是写出高效差集查询的前提。

五、总结与扩展

通过LEFT JOIN配合IS NULL,我们可以用标准SQL语法稳定地查出只存在于A表而不存在于B表的数据。它的核心在于理解左连接保留左表全量、右表无匹配补NULL的机制。在真实业务里,这种方法适用于数据核对、脏数据排查、异步流程漏单检测等场景。

当你掌握这种写法后,也可以进一步了解FULL OUTER JOIN模拟差集、以及使用EXCEPT关键字(部分数据库支持)的写法。但在跨数据库兼容的项目中,LEFT JOIN加IS NULL依旧是最省心的首选方案。

SQLLEFT_JOINIS_NULL修改时间:2026-08-05 10:42:30

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。