SQL向:用 NOT EXISTS 解决 NOT IN 和 NULL 之间的问题

数据介绍

假设现在有一张名为 DEPT 的表,它存在两列,分别代表部门名称与编号,如下所示,

ENAME_AND_DNAMEDEPTNO
ACCOUNTING10
RESEARCH20
SALES30
OPERATIONS40

还有一张名为 EMP 的表,它也存在两列,分别代表部门名称与编号,如下所示,

ENAME_AND_DNAMEDEPTNO
CLARK10
KING10
MILLER10

 

目的

你想找出在 DEPT 表中存在而在 EMP 表里却不存在的部门编号(如果有的话)。

 

方法

这里主要讲一下 MySQL 的方法

SELECT deptno 
from DEPT
WHERE deptno not in (SELECT deptno from EMP)

这里需要注意的几点是,

  • 使用 MySQL 的方法时,要注意这种方法保留了重复项,如果想要去除重复项,可以考虑使用 DISTINCT 关键字;
  • 在使用 NOT IN 时,要注意 Null 值;

此处着重讲一下第二点。

现在我们新建立一张名为 new_dept 的表。

deptno
10
50
null

如果你试着查询在 DEPT表里的数据,而不在 new_dept 表里的数据时,你会发现查不到任何值。

SELECT * 
from DEPT
WHERE deptno not in (SELECT deptno from new_dept)

这其中的原因在于 new_dept 表里存在 null 值。

上述 SQL 语句中的子查询会返回3条结果,它们的 deptno 分别为 10、50 和 null。IN 和 NOT IN 本质上是 OR 运算,由于 Null 值 参与 OR 逻辑运算的方式不同,IN 和 NOT IN 将会产生不同结果。针对这个结论,我们来看几个例子,

Example 1

select deptno
from dept
where deptno in ( 10,50,null )

返回结果为

 DEPTNO
 -------
 10

 

Example 2

select deptno
from dept
where (deptno=10 or deptno=50 or deptno=null)

返回结果为

 DEPTNO
 -------
 10

 

再来看看使用 NOT IN 和 NOT OR 的例子。

Example 3

select deptno
from dept
where deptno not in ( 10,50,null )

返回结果为

Empty set (0.00 sec)

Example 4

select deptno
from dept
where not (deptno=10 or deptno=50 or deptno=null)

返回结果为

Empty set (0.00 sec)

stackoverflow 上针对 NOT IN 和 NULL 连用存在的问题提出的疑问(https://stackoverflow.com/questions/129077/not-in-clause-and-null-values)。

为了避免 NOT IN 和 NULL 连用存在的问题,我们可以考虑结合使用 NOT EXISTS 和 关联子查询。关联子查询指的是外层查询执行后获得的结果集会被内层子查询引用。

select d.deptno
from dept d
where not exists ( select null
from emp e
where d.deptno = e.deptno )

返回结果为

+--------+
| deptno |
+--------+
|     10 |
|     50 |
|   NULL |
+--------+

上述语句遍历并评估 dept 表的每一行。针对每一行,都会有如下操作。

1. 执行子查询并检查当前的部门编号是否存在于 EMP 表。要注意关联条件 D.DEPTNO = E.DEPTNO,它通过部门编号把两个表连接起来。

2. 如果子查询有结果返回给外层查询,那么 EXISTS (...) 的评估结果是 TRUE,这样 NOT EXISTS (...) 就是 FALSE,如此一来,外层查询就会舍弃当前行。

3. 如果子查询没有返回任何结果,那么 NOT EXISTS (...) 的评估结果是 TRUE,由此外层查 询就会返回当前行(因为它是一个不存在于 EMP 表中的部门编号)。

Note:把 EXISTS/NOT EXISTS 和关联子查询一起使用时,SELECT 列表里的项目并不重要,因此这个例子中用了 SELECT NULL,这是为了把注意力放到子查询的连接操作上,而非 SELECT 列表的项目上。

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值