SQL避坑:当in,not in遇上null这种坑你避过吗?

目录

0 数据准备

1 分析

案例1:当过滤条件中in里面包含null

案例2:当主表中有null,采用not in且not in后面不为子查询的时候会忽略NULL,匹配不到NULL,即查询不到任何NULL的记录

案例3:当not in中包含NULL

 案例4:如果not in后面跟子查询时,所选的字段包含NULL值要特别注意

2 小结

0 数据准备

create table t as 
select '张三' as name,1 as class
union all
select '李四' as name,1 as class
union all
select '王五' as name,2 as class
union all
select '老六' as name,null as class
union all
select '陈七' as name, 4 as class
union all
select '赵八' as name, 5 as class
union all
select '孙九' as name, 5 as class

1 分析

原表数据如下:

扫描二维码关注公众号,回复: 14834212 查看本文章

案例1:当过滤条件中in里面包含null

select * from t where class in (null,'1')

in相当于or,对于null值得判断不是is null而是等于,因而对于NULL值得判断永远都是false

select * from t where class ='1' or class =null

 

如果想得到NULL的结果,一定要对NULL的值做转换,可以采用如下SQL

select * from t where coalesce(class,'null') in ('null','1')

 

结论:当in中包含 null,不会获取NULL的结果

案例2:当主表中有null,采用not in且not in后面不为子查询的时候会忽略NULL,匹配不到NULL,即查询不到任何NULL的记录

select * from t where class not in ('1','2')

案例3:当not in中包含NULL

select * from t where class not in ('1','2',null)


not in 相当于and条件,只要有一个false,那么所有的都为false我们知道in not in都是用等号(=)来判断的,对于null不会使用is not null那么上面改写为:

select * from t where class !='1'and !='2'and !=null

结果如下:


由于判读不等于NULL永远为false,所有整个条件判断都为false,where中为flase
那么结果将什么也不会返回对于 要过滤null的情况,可以这样改写,将null转换为字符串'null',SQL如下:

select * from t where coalesce(class,'null') not in ('1','2','null') 

 案例4:如果not in后面跟子查询时,所选的字段包含NULL值要特别注意

如下SQL:

select *
from t
where class not in (
    select class
    from t t1
    where coalesce(t1.class, 'null') in (1, 2, 'null'))

 最终返回结果为空,原因是子查询中包含了NULL值,对于这种情况一定要特别注意,当not in中包含子查询时,如果结果中包含NULL值,将查不到任何结果,此时应该需要主动在子查询中将NULL值过滤掉。

create table t1 as
select 1 as class
union all
select 2 as class
union all
select 3 as class
union all
select 4 as class
union all
select null as class
union all
select null as class

select *
from t
where class not in (
    select class
    from t1
    )

最终结果为空

 正确的写法如下:

select *
from t
where class not in (
    select class
    from t1
    where class is not null
    )

或者用not exists代替not in.注意exists函数和in使用方法不一样。

exists实际上是一个高阶谓词,所谓的高阶是指他的输入行数往往是多行,一个集合,和其他谓词判断不一样,其他的谓词一般输入值是一个标量值(单一值),我们知道 EXISTS 的参数是行数据的集合,之所以这么说,是因为无论子查询中选择什么样的列,对于 EXISTS 来说都是一样的。在 EXISTS 的子查询里, SELECT 子句的列表可以有下面这三种写法。

select *
from t
where NOT EXISTS (
    select t1.class
    from t1
where t.class=t1.class
    )

====================
select *
from t
where NOT EXISTS (
    select 1
    from t1
where t.class=t1.class
    )
====================
select *
from t
where NOT EXISTS (
    select *
    from t1
where t.class=t1.class
    )

 结果如下:

OK
t.name  t.class
老六    NULL
孙九    NULL
赵八    5
Time taken: 39.269 seconds, Fetched: 3 row(s)

用个图来概括下一般的谓词与 EXISTS 的区别

此处有个问题,我们子查询中的结果如下:

select class
    from t1
    where class is not null

那么上述的结果与如下SQL等价吗?

select * from t where class not in (1,2,3,4)

 执行上述SQL:我们得到的结果如下

 显然两个SQL不等价,也就是说当not in 中有子查询的时候,主表中的NULL不会忽略,可以得到主表NULL的结果,当not in中是常量值得时候,主表中的NULL被忽略,这个略有点坑。

如果非要过滤掉NULL值,可以按如下写法 

select *
from t
where coalesce(class,'null') not in (
    select coalesce(class,'null')
    from t1
    )

2 小结

  • (1) 当in中过滤条件中存在NULL,无论in后面跟常量还是子查询主表中的NULL不会被过滤,此时需要 过滤可以用COALESCE()函数将NULL值转换
  • (2) 当主表中存在NULL,采用not in的时候,如果not in后面跟常量,筛选的结果中不会存在NULL,  就是会忽略NULL值,如果not in后面跟子查询,筛选的结果中会存在NULL值,很神奇,如果需要 NULL存在,则进行转换
  • (3) 当not in中包含NULL值,无论后面跟常量还是子查询,将不会有任何结果返回,这点一定要注意, 特别为子查询的时候需要根据业务需求将NULL进行转换或过滤NULL值

总之用in的时候一般问题不大,使用not in的时候需要特别主要,结合本文恰当使用,之所以
造成如此大的差异原因是对NULL的判断上,NULL的判断本质上is null或is not null,in或
not in的判断是等于(=),所以对NULL判断是无效的,需要谨记。

猜你喜欢

转载自blog.csdn.net/godlovedaniel/article/details/125376244