NOT IN 的 NULL 陷阱:一次 UNION 数据"神秘消失"

昨天写了个 SQL,逻辑明明没毛病,数据就是查不出来。排查半天,元凶竟是 NULL

一、现象:我的数据呢?

先看我写的 SQL,很简单:

SQL 复制代码
WITH tmp_union3 AS (
    SELECT * FROM tmp_union
    UNION ALL
    SELECT * FROM tmp_union2
    WHERE id NOT IN (
        SELECT DISTINCT id FROM tmp_union
    )
)
SELECT * FROM tmp_union3 WHERE id = '23009724';

已知:

  • tmp_union 里没有 '23009724'
  • tmp_union2明明有 '23009724'

按道理,NOT IN 应该放行,然后 UNION ALL 合并,最后查出来。

结果:查无此记录。

二、排查:问题出在哪?

先单独跑 NOT IN 这一段:

SQL 复制代码
SELECT * FROM tmp_union2
WHERE id NOT IN (SELECT DISTINCT id FROM tmp_union);

返回:空结果集

'23009724' 明明不在 tmp_union 里,凭啥被过滤了?

再查一下 tmp_union 的数据:

SQL 复制代码
SELECT id FROM tmp_union;

破案了------tmp_union 里有 NULL

三、原理:一个 NULL,全军覆没

在 PostgreSQL 里,没有unknown,只有null,NULL 代表"未知"

NOT IN 的右侧出现 NULL 时:

SQL 复制代码
'2026' NOT IN (NULL, '2025', '2026')

数据库实际比较:

SQL 复制代码
'2026' = '2025'  → FALSE
'2026' = NULL        → NULL   -- 不是 FALSE,是 NULL!

关键点来了:

NOT IN 的本质是 AND 串联的等值比较

Plain 复制代码
'2026' <> NULL AND '2026' <> '2025' AND ...

在 PG 里,NULL AND FALSE 的结果是 NULLNULL AND TRUE 也是 NULL

WHERE 子句只认 TRUENULL 被当成不满足条件。

所以只要 NOT IN 的右侧集合里有一个 NULL,整个条件就塌了,所有行都被过滤。

不是一个,是全部。这就是黑洞。

四、修复:三选一

方法 1:过滤 NULL(改动最小)

SQL 复制代码
WHERE id NOT IN (
    SELECT DISTINCT id FROM tmp_union
    WHERE id IS NOT NULL  -- 加这一行
)

方法 2:改用 NOT EXISTS(推荐)

SQL 复制代码
SELECT * FROM tmp_union2 t2
WHERE NOT EXISTS (
    SELECT 1 FROM tmp_union t1
    WHERE t1.id = t2.id
);

NOT EXISTS 不受 NULL 影响,而且性能通常更好。

方法 3:LEFT JOIN 取差集

SQL 复制代码
SELECT t2.*
FROM tmp_union2 t2
LEFT JOIN tmp_union t1 ON t1.id = t2.id
WHERE t1.id IS NULL;

五、口诀

看到 NOT IN,先防 NULL;能写 EXISTS,别写 IN

六、总结

这次踩坑就一句话:

NOT IN 遇到 NULL,不是漏几条,是全部消失。

而且不会报错,悄无声息。

你以为是逻辑问题,其实是 NULL 在搞鬼。

以后写 SQL,遇到 NOT IN 多长个心眼,或者直接换成 NOT EXISTS,一劳永逸。

你遇到过类似的 NULL 陷阱吗?评论区聊聊,一起排雷。

相关推荐
basketball6161 小时前
Go语言从入门到进阶:8. 接口
开发语言·后端·golang
明月_清风1 小时前
深入 Go 并发编程:从 Goroutine 到 Channel 的系统性避坑指南
后端·go
雪隐1 小时前
AI股票小助手04-miniQMT数据采集
人工智能·后端
苏三说技术1 小时前
MybatisPlus Pro 来了,CURD开发效率直接拉满!
后端
小江的记录本1 小时前
【JVM虚拟机】类加载机制:类加载器、双亲委派模型、好处、破坏双亲委派的场景(附《思维导图》+《面试高频考点清单》)
java·jvm·spring boot·后端·python·spring·面试
李少兄1 小时前
Spring 对象创建范式:依赖注入与直接实例化的边界抉择
java·后端·spring
二月龙2 小时前
SpringBoot 简化开发的核心原理:告别繁琐配置
后端
Java内核笔记2 小时前
Spring Security 过滤器链全景图:从 FilterOrderRegistration 到实战配置
后端
文心快码BaiduComate2 小时前
Comate搭载MiniMax M3:支持超长百万上下文
前端·人工智能·后端