在写SQL语句的时候,若where条件是判断用户不在某个集合当中,我们习惯使用 where 列名 not in (集合) 子句,这种写法本身没有问题,但实践过程中却发现很多人在写类似的SQL语句时,写的代码存在隐患,而这种隐患往往难以发现。

1. 存在隐患的写法

首先,我们来评估一条简单的SQL语句的输出结果。语句如下:

select 1 from dual where 1 not in(2, null)

简单,输出结果是1嘛。

错!答案是没有输出结果。

为什么会这样?数据库管理系统在执行查询之前,会对上面语句进行简单的转化,转化之后的语句如下:

select 1 from dual where 1 != 2 and 1 != null

显然,where后面的表达式中,1!=2的结果是true,但1!=null的结果是不确定true不确定进行与运算的结果非true,所以上述语句的没有输出结果。

分析完了上面这条语句,我们再来看一个问题,假设有员工表DEMPLOYEES和部门表DEPARTMENTS,写SQL语句找出2015年没有招人的部门。我们可以很快的写出下面语句:

select DEPARTMENT_ID, DEPARTMENT_NAME from DEPARTMENTS where DEPARTMENT_ID not in(
select DEPARTMENT_ID from EMPLOYEES where HIRE_DATE like '%2015%'
)

若员工表EMPLOYEES中存在还未分配部门的员工时,子查询中的结果集中会含NULL元素,这就和开始我们评估的语句套上了,语句将无结果返回。那么,该如何写这种形式的语句才能避免隐患呢?

2. NOT IN 子句正确的打开方式

为了避免NOT IN子句的隐患,最简单的方式就是不使用NOT IN子句,而使用其他的形式达到相同的效果。你老是出问题,我不跟你玩行了吧!例如使用 not exists子句将上述语句改写正确之后代码如下:

select DEPARTMENT_ID, DEPARTMENT_NAME from DEPARTMENTS where not exists (
select * from EMPLOYEES where EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID and HIRE_DATE like '%2015%'
)

若还是想用 NOT IN子句,怎么办?有方法,就是对子查询返回的结果进行处理,如果为空值NULL,则给它赋一个非空值。这种对空值NULL的处理,各类数据库管理系统都有相应函数函数支持,例如SQL Server中的 ISNULL函数,Oracle 中的NVLNVL2COALESE,以及MySQL中的IFNULL , COALESE

以SQL Server为例,如下SQL语句也能够像NOT EXISTS子句一样避免隐患。

select DEPARTMENT_ID, DEPARTMENT_NAME from DEPARTMENTS where DEPARTMENT_ID != all(
select ISNULL(DEPARTMENT_ID,'') from EMPLOYEES where HIRE_DATE like '%2015%'
)

3. 小结

在使用NOT IN子句时,若子查询结果集中可能包含空值NULL,则代码存在隐患,要消除这种隐患,应该对子查询结果集中的空值进行处理。

附:基于SQL Server的完整实验代码

假设公司ERP系统中有两个表,员工表EMPLOYEES和部门表DEPARTMENTS(如下所示),老板要找出2015年没有招人的部门号和部门名称。请写出查询的SQL语句。

员工表 EMPLOYEES

EMPLOYEE_ID EMPLOYEE_NAME HIRE_DATE DEPARTMENT_ID
170101 Bob 2016-02-02 001
170102 Alice 2015-02-05 003
170103 Tony 2016-03-04 002
170105 Aaron 2016-08-03 002
170107 Rex 2016-10-11 NULL

部门表 DEPARTMENTS

DEPARTMENT_ID DEPARTMENT_NAME MANAGER_ID
001 Administration 170101
002 IT 170103
003 Finance 170102

创建表SQL语句。

CREATE TABLE DEPARTMENTS(
DEPARTMENT_ID CHAR(3) PRIMARY KEY,
DEPARTMENT_NAME VARCHAR(100),
MANAGER_ID CHAR(6)
); CREATE TABLE EMPLOYEES(
EMPLOYEE_ID CHAR(6) PRIMARY KEY,
EMPLOYEE_NAME VARCHAR(30) NOT NULL,
HIRE_DATE DATE,
DEPARTMENT_ID CHAR(3),
FOREIGN KEY(DEPARTMENT_ID) REFERENCES DEPARTMENTS(DEPARTMENT_ID)
); insert into DEPARTMENTS values('','Administration','');
insert into DEPARTMENTS values('','IT','');
insert into DEPARTMENTS values('','Finance',''); insert into EMPLOYEES values('', 'Bob', '2016-03-02', '');
insert into EMPLOYEES values('', 'Alice', '2015-02-05', '');
insert into EMPLOYEES values('', 'Tony', '2016-03-04', '');
insert into EMPLOYEES values('', 'Aaron', '2016-08-03', '');
insert into EMPLOYEES values('', 'Rex', '2016-10-11', NULL);

展开代码

上述题目,我们很容易产生这样的思路,先在员工表 EMPLOYEES 中找出2015年雇佣的员工所在的部门号,作为一个部门号子集合,然后在部门表 DEPARTMENTS 中找出不在该子集中的部门号和部门ID,即为要查找的结果。于是有了如下查询SQL语句:

select DEPARTMENT_ID, DEPARTMENT_NAME from DEPARTMENTS where DEPARTMENT_ID not in(
select DEPARTMENT_ID from EMPLOYEES where HIRE_DATE like '%2015%'
)

然而,由于子查询中存在一个空值,所以SQL Server数据库管理系统执行上述语句之后将返回0条结果。而实际上我们可以从上表中看到,2015年没有招人的部门号和部门名称有{001, Administration } 部门,故上面的查询语句存在BUG。

为保证查询出我们期望的结果,这里使用SQL Server的ISNULL函数对子查询中的空值进行处理,处理之后SQL语句为:

select DEPARTMENT_ID, DEPARTMENT_NAME from DEPARTMENTS where DEPARTMENT_ID not in(
select ISNULL(DEPARTMENT_ID,'') from EMPLOYEES where HIRE_DATE like '%2015%'
)

SQL Server DBMS将返回给我们期望的结果:

DEPARTMENT_ID    DEPARTMENT_NAME
001       Administration
002 IT

SQL语句中 NOT IN 子句的“正确打开方式”的更多相关文章

  1. SQL语句中的Having子句与where子句

    一.介绍 聚合函数如SUM, COUNT, MAX, AVG等.这些函数和其它函数的根本区别就是它们一般作用在多条记录上.而通过使用GROUP BY 子句,可以让SUM 和 COUNT 这些函数对属于 ...

  2. sql语句中where,have,on的区别

    一.where和on的区别 数据库在通过连接两张或多张表来返回记录时,都会生成一张中间的临时表,然后再将这张临时表返回给用户.  在使用left jion时,on和where条件的区别如下:   1. ...

  3. SQL语句中过滤条件放在on和where子句中的区别和联系

    摘要: 介绍在多表关联SQL语句中,过滤条件放在on和where子句中的区别--inner join中没区别,外连接就不一样. 综述   蚂蚁金服的一道SQL面试题如下:SQL语句中,过滤条件放在on ...

  4. sql语句中----删除表数据drop、truncate和delete的用法

    sql语句中----删除表数据drop.truncate和delete的用法 --drop drop table  tb   --tb表示数据表的名字,下同 删除内容和定义,释放空间.简单来说就是把整 ...

  5. (转载)SQL语句中Group by语句的详细介绍

    转自:http://blog.163.com/yuer_d/blog/static/76761152201010203719835 SQL语句中Group by语句的详细介绍              ...

  6. SQL点滴35—SQL语句中的exists

    原文:SQL点滴35-SQL语句中的exists 比如在Northwind数据库中有一个查询为 SELECT c.CustomerId,CompanyName FROM Customers c WHE ...

  7. SQL Server参数化SQL语句中的like和in查询的语法(C#)

    sql语句进行 like和in 参数化,按照正常的方式是无法实现的 我们一般的思维是: Like参数化查询:string sqlstmt = "select * from users whe ...

  8. sql语句中left join、right join 以及inner join之间的使用与区别

    sql语句中left join.right join 以及innerjoin之间的使用与区别 left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录  right join( ...

  9. SQL语句中的单引号处理以及模糊查询

    为了防止程序SQL语句错误以及SQL注入,单引号必须经过处理.有2种办法: 1.使用参数,比如SELECT * FROM yourTable WHERE name = @name; 在C#中使用Sql ...

随机推荐

  1. 大数据学习(2)- export、source(附带多个服务器一起启动服务器)

    linux环境中, A=1这种命名方式,变量作用域为当前线程 export命令出的变量作用域是当前进程及其子进程. 可以通过source 脚本,将脚本里面的变量放在当前进程中 附带自己写的tomcat ...

  2. SQL Join的应用(转)

    INNER JOIN LEFT JOIN RIGHT JOIN OUTER JOIN LEFT JOIN EXCLUDING INNER JOIN RIGHT JOIN EXCLUDING INNER ...

  3. git 的用法和命令

    学无止境,精益求精! 十年河东,十年河西,莫欺少年穷! 学历代表你的过去,能力代表你的现在,学习代表你的将来! 很久没写博客了,都是工作太忙闹的,索性今儿转发一篇!省的博客园太冷清了... Git图形 ...

  4. JavaScript函数尾调用与尾递归

    什么是函数尾调用和尾递归 函数尾调用与尾递归的应用 一.什么是函数的尾调用和尾递归 函数尾调用就是指函数的最后一步是调用另一个函数. //函数尾调用示例一 function foo(x){ retur ...

  5. Idea格式化快捷键无效,没反应

    Idea格式化快捷键无效,没反应 1,关闭网易云音乐快捷键 2,修改搜狗输入法快捷键 目前本人只遇到过这两种

  6. ubuntu 共享WIFI并分享主机的代理服务

    背景是这样的: 公司内的主机访问外网需要通过一个HTTP代理服务器,主机ubuntu共享wifi给手机使用的时候需要在手机上配置一个代理才能访问互联网. 我觉得这样比较麻烦,所以想在主机上直接把共享w ...

  7. 逆向破解H.Koenig 遥控器 Part 2

    逆向破解H.Koenig 遥控器 Part 2 到目前为止,我们已经知道了RF收发器的安装过程,下面是我们所掌握的东西 : l 无线电收发器是一个Avantcom A7105 l 调制是FSK l 频 ...

  8. Spinner simpleAdapte适配器 下拉列表

    public class MainActivity extends AppCompatActivity { private TextView text; private Spinner spinner ...

  9. dubbo 框架

    2.1 dubbo  Apache Dubbo (incubating) |ˈdʌbəʊ| 是一款高性能.轻量级的开源 Java RPC 框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和 ...

  10. ubuntu下mysql的用户添加、授权、取消授权

    一.添加用户 新增用户会有两种方式的,一种是使用create命令,另一种是直接回使用grant 命令 create user 名字@登陆地址 identified by "密码"; ...