注册
not in遇到null是如何形成大坑的
专栏/滴水藏海/ 文章详情 /

not in遇到null是如何形成大坑的

yuao 2022/12/21 2302 4 2
摘要 当你的数据中存在null并且sql中有not in语法的时候你就要注意了。。。

    有项目中客户提出sql的执行结果“错误”,没有按照预期得出结果,并且说很容易重现,大致就是说要找a表中有但是b表中没有的数据。最后发现是子查询中有null导致的,其实结果并没有错,只是使用方法进入误区了,在此记录一下这个很简单但是容易搞错的用法。
先构造一个简单的例子,创建两个表分表插入一条记录:

create table t1(c1 varchar(20));
insert into t1 values('a');
commit;
create table t2(c2 varchar(20));
insert into t2 values('b');
commit;

下面我们查询是否存在t1中但是t2中没有的数据,即t1相对于t2的差集。

select 1 from t1 where c1 not in(select c2 from t2);

image.png

select 1 from t1 where not exists (select 1 from t2 where t1.c1=t2.c2);

image.png

可以看到此时结果是符合预期的,此时在向t2中插入一个null值并查询
image.png
此时可以看到没有返回结果。
看似该结果并不是我们的预期结果,但实际上是进入了误区。由于t2表中插入了一条null值,那么我们知道达梦数据库中的null值表示“未知的值”,也就是说它有可能是任何值,也可能不是任何值,是一个很特殊的状态,这一点跟oracle是一致的。对于null值我们不能用普通的算术运算符去做比较,例如=null,这是不对的,null必须使用is或者is not进行比较。
    那我们把上述例子等价改下一下相当于:

select 1 from dual where 'a'<>'b' and 'a'<>null;

其中’a’<>null永远为false,所以这个sql并不会返回结果。
    那对于一些内表数据量很大的情况下,我们会改写成not exists的形式,我们来看一下结果是否符合预期:

select 1 from t1 where not exists (select 1 from t2 where t1.c1=t2.c2);

image.png
可以看到对于内表中有null的情况,not exists并不会影响到结果。
总结:
    日常使用not in 进行子查询时,我们一定要注意子查询中是否有null,否则就会导致执行结果并不是自己预期的。为了安全起见,我们可以在子查询中过滤掉null或者使用not exists来规避内表的null问题。如下两种形式:

select 1 from t1 where c1 not in(select c2 from t2 where c2 is not null);
select 1 from t1 where not exists (select 1 from t2 where t1.c1=t2.c2);
评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服