在写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. Scala学习七——包和引入

    一.本章要点 包也可也可以像内部类那样嵌套 包路径不是绝对路径 包声明链x.y.z并不自动将中间包x和x.y变成可见 位于文件顶部不带花括号的包声明在整个文件范围内有效 包对象可以持有函数和变量 引入 ...

  2. C#面向对象14 List泛型集合/装箱和拆箱/字典集合(Dictionary)

    1.List泛型集合 using System; using System.Collections.Generic; using System.Linq; using System.Text; usi ...

  3. nop4.1学习ServiceCollectionExtensions(二)(ioc,ef,ef连接的实现)

    这个是获取程序路径,并初始化文件管理类 初始化插件管理 接下来就是注册服务和autoafc 在INopStartup配置sql连接,插件,mvc等 配置了sql连接 数据库的配置类 在AddAutoM ...

  4. C#Linq之求和,平均值,最大值,最小值

    using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...

  5. UVA10603Fill题解--BFS

    题目链接 https://cn.vjudge.net/problem/UVA-10603 分析 经典的倒水问题,直接BFS. 对于喜闻乐见的状态判重,一开始想来个哈希函数把一个三元组映射成一个数,后面 ...

  6. 查询SQL Server版本号

    一.查看SQL Server 2005版本号 SELECT @@VERSION 显示以下信息: Microsoft SQL Server 2005 - 9.00.3042.00 (Intel X86) ...

  7. 第二十四篇 jQuery 学习6 删除元素

    jQuery 学习6 删除元素   上节课我们做了添加元素,模拟的是楼主发的文章,路人评论,那么同学们这节课学了删除之后,去之前的代码上添加一个删除,模拟一个楼主删除路人的评论. jQuery的删除方 ...

  8. SQL练习汇总

    --1.选择部门30中的所有员工. --2.列出所有办事员(CLERK)的姓名,编号和部门编号. select ename,empno,deptno from emp where job='CLERK ...

  9. 十,StatefulSet简介及简单使用

    目录 StatefulSet简介 为什么要用statefulset控制器 简单测试 使用 StatefulSet 创建基础的PV StatefulSet 清单 statefulset管理pod的启停顺 ...

  10. mysql数据库:分表、多表关联、外键约束、级联操作

    一.分表.外键.级联.多对一 二.多对多 三.一对一 一.分表.外键.级联.多对一 将部门数据与员工数据放到同一张表中会造成 数据重复 结构混乱 扩展维护性差 需要分表 create table de ...