在数据库中存在一种特殊的值:NULL(空值)。一个字段如果没有被赋值,那么它的值就是NULL,NULL并不代表没有值而是表示值未知。员工信息表中存储着身份证号、姓名、年龄等信息,其中某条记录中年龄字段的值为NULL,并不表示这个员工没有年龄,而只是他的年龄暂时不知道。因此,在数据库中NULL 主要用于标识一个字段的值为“未知”。

由于NULL在数据库中是比较特殊的,所以在涉及到NULL的一些处理中也会存在一些需要特别注意的地方。为了更加清晰的讲解我们将创建一张表,执行下面的SQL语句:


MYSQL,MSSQLServer,DB2: CREATE TABLE T_Employee ( FId VARCHAR(20), FName VARCHAR(20), FSalary INT ) Oracle: CREATE TABLE T_Employee ( FId VARCHAR2(20), FName VARCHAR2(20), FSalary NUMBER (10) )

T_Employee表保存了员工信息,FId字段为主键,FName字段为员工姓名,FSalary字段为员工工资。请在相应的DBMS 中执行相应的SQL 语句,然后执行下面的SQL语句向T_Employee表中插入一些演示数据:


INSERT INTO T_Employee(FId,FName,FSalary)VALUES(‘1’,‘Tom’,3000); INSERT INTO T_Employee(FId,FName,FSalary)VALUES(‘2’,‘Jim’,NULL); INSERT INTO T_Employee(FId,FName,FSalary)VALUES(‘3’,NULL,8000); INSERT INTO T_Employee(FId,FName,FSalary)VALUES(‘4’,‘Lily’,9000); INSERT INTO T_Employee(FId,FName,FSalary)VALUES(‘5’,‘Robert’,2000);

执行完毕查看T_Employee表中的内容:


FId FName FSalary 1 Tom 3000 2 Jim <NULL> 3 <NULL> 8000 4 Lily 9000 5 Robert 2000
  • NULL与比较运算符

NULL 表示未知的值,因此在使用比较运算符的时候就需要注意NULL 值可能造成的BUG。比如有的开发人员认为下面的SQL 语句将返回Jim、Robert、Tom 三个人的工资,因为他认为NULL等于0:


SELECT * FROM T_Employee WHERE FSalary<5000

可是执行上面的查询语句后却得到了下面的结果:


FId FName FSalary 1 Tom 3000 5 Robert 2000

Jim并没有像预想的那样被检索出来。这是因为NULL不等于0,它代表“未知”,Jim的工资未知,所以DBMS不会认为它的工资小于5000,所以它并不会被检索出来。有的开发人员认为下面的SQL 语句将返回所有员工的工资,因为所有员工的工资肯定不是大于5000 就是小于等于5000:


SELECT * FROM T_Employee WHERE FSalary<5000 OR FSalary>=5000

可是执行上面的查询语句后却得到了下面的结果:


FId FName FSalary 1 Tom 3000 3 <NULL> 8000 4 Lily 9000 5 Robert 2000

同样,Jim并没有像预想的那样被检索出来。因为貌似这个查询条件包含了所有的工资金额,可以DBMS是无法确认NULL 值是不是在这个范围之内的,因此Jim并不会被检索出来。

因此为了检索所有工资小于5000 元的员工,包括工资额未知的员工,必须使用ISNULL运算符,SQL语句如下:


SELECT * FROM T_Employee WHERE FSalary<5000 OR FSalary IS NULL

执行完毕我们就能在输出结果中看到下面的执行结果:


FId FName FSalary 1 Tom 3000 2 Jim <NULL> 5 Robert 2000
  • NULL和计算字段

如果NULL 值出现在任何计算字段中,那么计算结果永远是NULL。为了验证这一点请执行下面的SQL语句:


SELECT FId,FName, FSalary ,FSalary+2000 FROM T_Employee

执行完毕我们就能在输出结果中看到下面的执行结果:


FId FName FSalary FSalary+2000 1 Tom 3000 5000 2 Jim <NULL> <NULL> 3 <NULL> 8000 10000 4 Lily 9000 11000 5 Robert 2000 4000

第二行记录的FSALARY 字段为NULL,为一个未知的工资增加2000 元得到的仍然是未知工资NULL,这是完全符合逻辑的。

如果这个结果不符合业务系统的要求可以通过两种方式来解决这个问题,一个是过滤掉NULL值,一个是将NULL值转换为业务系统认为的值。

第一种解决方式例子如下,这里用IS NOT NULL运算符将NULL值过滤掉:


SELECT FId,FName, FSalary ,FSalary+2000 FROM T_Employee WHERE FSalary IS NOT NULL

执行完毕我们就能在输出结果中看到下面的执行结果:


FId FName FSalary FSalary+2000 1 Tom 3000 5000 3 <NULL> 8000 10000 4 Lily 9000 11000 5 Robert 2000 4000

第二种解决方式例子如下,这里使用CASE 函数将NULL值转换为0,也就是认为工资未知的工资为0:


SELECT FId,FName, FSalary , ( CASE WHEN FSalary IS NULL THEN 0 ELSE FSalary END )+2000 FROM T_Employee

执行完毕我们就能在输出结果中看到下面的执行结果:


FId FName FSalary 1 Tom 3000 5000 2 Jim <NULL> 2000 3 <NULL> 8000 10000 4 Lily 9000 11000 5 Robert 2000 4000
  • NULL和字符串

如果NULL值出现在任何和字符串相关计算字段中,那么计算结果永远是NULL。为了验证这一点请执行下面的SQL语句:


MYSQL,Oracle: SELECT FId,FName,FName||‘LOL’,FSalary FROM T_Employee MSSQLServer: SELECT FId,FName,FName+‘LOL’,FSalary FROM T_Employee DB2: SELECT FId,FName,CONCAT(FName,‘LOL’),FSalary FROM T_Employee

执行完毕我们就能在输出结果中看到下面的执行结果:


FId FName FSalary 1 Tom TomLOL 3000 2 Jim JimLOL <NULL> 3 <NULL> <NULL> 8000 4 Lily LilyLOL 9000 5 Robert RobertLOL 2000

第三行记录的FName 字段为NULL,为一个未知姓名的员工的名字后增加“LOL”得到的仍然是未知姓名NULL,这是完全符合逻辑的。

如果这个结果不符合业务系统的要求,同样可以采用10.6.2 的解决方案,这里不再赘述。

  • NULL和函数

如果NULL 值出现在普通函数中,那么计算结果永远是NULL。为了验证这一点请执行下面的SQL语句:


SELECT FId,FName, FSalary ,ABS(FSalary-5000) FROM T_Employee

执行完毕我们就能在输出结果中看到下面的执行结果:


FId FName FSalary 1 Tom 3000 2000 2 Jim <NULL> <NULL> 3 <NULL> 8000 3000 4 Lily 9000 4000 5 Robert 2000 3000

第二行记录的FSalary字段为NULL,对一个未知值进行函数计算得到的仍然是未知NULL,这是完全符合逻辑的。

如果这个结果不符合业务系统的要求,同样可以采用10.6.2 的解决方案,这里不再赘述。

  • NULL和聚合函数

和普通的函数不同,如果NULL值出现在聚合函数中,那么NULL值将会被忽略。

为了验证这一点请执行下面的SQL语句:


SELECT MAX(FSalary) AS MAXSALARY,MIN(FSalary) AS MINSALARY,COUNT(FSalary) FROM T_Employee

执行完毕我们就能在输出结果中看到下面的执行结果:


MAXSALARY MINSALARY 9000 2000 4

按照前面的分析,一个包含NULL值在内的所有员工工资的的最大值和最小值应该是未知NULL,不过聚合函数是一个例外,NULL值将会被忽略。这是需要特别注意的。

  • 诀窍

处理含有NULL值的运算是非常麻烦的,不过只要记住“NULL代表未知”这一原则就可以灵活应对很多问题。下面举几个例子:条件表达式“NULL=3”的返回值为NULL,因为无法确认3是否与一个未知值相等;“NULL=NULL”的返回值也为NULL,因为无法确认两个未知值是否相等;“NULL<>NULL”的返回值也为NULL,因为同样无法确认两个未知值是否不相等。

表达式“NULLAND TRUE”的返回值为NULL,因为无法确认一个未知值与TRUE进行AND运算的结果;表达式“NULLAND FALSE”的返回值为TRUE,因为任何一个布尔值与FALSE 进行AND 运算的结果都为TRUE,虽然NULL 表示未知值,但是NULL同样不是TRUE 就是FALSE;表达式“NULL OR TRUE”的返回值为TRUE,因为任何一个布尔值与TRUE进行OR运算的结果都为TRUE,虽然NULL表示未知值,但是NULL同样不是TRUE就是FALSE;表达式“NULL OR FALSE”的返回值为TRUE,因为无法确认一个未知值与FALSE 进行OR运算的结果。

NULL的学问的更多相关文章

  1. 【程序员的SQL金典】笔记(第6章~第11章)

        第六章 索引与约束   1.索引用来提高数据的检索速度,而约束则用来保证数据的完整性.   2.创建索引 创建索引的SQL 语句是CREATE INDEX,其语法如下: CREATE INDE ...

  2. SQL金典

    ps:补充自己的基础知识,大神请无视.. ~~~~~~~~~~~~~~~~~~~~~ DataBase Management System,DBMS.... Catalog ...库 Table... ...

  3. 《深入理解JAVA虚拟机》笔记1

    java程序运行时的内存空间,按照虚拟机规范有下面几项: )程序计数器 指示下条命令执行地址.当然是线程私有,不然线程怎么能并行的起来. 不重要,占内存很小,忽略不计. )方法区 这个名字很让我迷惑. ...

  4. 学问Chat UI(3)

    前言 上文学问Chat UI(2)分析了消息适配器的实现; 本文主要学习下插件功能如何实现的.并以图片插件功能作为例子详细说明,分析从具体代码入手; 概要 分析策略说明 "+"功能 ...

  5. 【小计】新人Tostring前忘记Null判断的处理

    ToString和string.Concat(可屏蔽Null的异常)性能相差不大,一些中小项目完全可以用Concat(新人容易忘记判断Null的情况,遇到太多了,所以建议重写tostring方法,内部 ...

  6. SQL Server-聚焦NOT IN VS NOT EXISTS VS LEFT JOIN...IS NULL性能分析(十八)

    前言 本节我们来综合比较NOT IN VS NOT EXISTS VS LEFT JOIN...IS NULL的性能,简短的内容,深入的理解,Always to review the basics. ...

  7. 异步 HttpContext.Current 为空null 另一种解决方法

    1.场景 在导入通讯录过程中,把导入的失败.成功的号码数进行统计,然后保存到session中,客户端通过轮询显示状态. 在实现过程中,使用的async调用方法,出现HttpContext.Curren ...

  8. js中的null 和undefined

    参考链接:http://blog.csdn.net/qq_26676207/article/details/53100912 http://www.ruanyifeng.com/blog/2014/0 ...

  9. JavaScript中undefined与null的区别

    通常情况下, 当我们试图访问某个不存在的或者没有赋值的变量时,就会得到一个undefined值.Javascript会自动将声明是没有进行初始化的变量设为undifined. 如果一个变量根本不存在会 ...

随机推荐

  1. postgreysql

    基础 syntax * \help 生成所有的pg命令 * abort 终止事务/work * alter aggregate 修改聚合函数的定义 ALTER AGGREGATE name ( typ ...

  2. 新手用WPF山寨QQ管家7.6(三)

    由于一直忙工作,没有更新完博客,更可恨的是...在清理资料的时候不小心删除了之前自己做的各种效果的DEMO....好在项目中用到了大部分,也算有所保留,以后可不敢随便删东西了....太可怕了! 在 新 ...

  3. HDU 4919 Exclusive or 数学

    题意: 定义 \[f(n)=\sum\limits_{i=1}^{n-1}(i\oplus (n-i))\] 求\(f(n),n \leq 10^{500}\) 分析: 这个数列对应OEIS的A006 ...

  4. Linux QA

    gitee: https://gitee.com/dhclly/icedog.script.test/blob/master/doc/linux/linux-qa.md 1. linux 中的 ll( ...

  5. 如何在Linux下使用Rsync

    如何在Linux下使用Rsync 吐槽 昨天对scp进行总结之后看到最后有说到Rsync,俗语有云:好奇心害死猫.抱着学习的态度将Rsync给找了出来,然后进行了一些简单的学习.下面介绍一些个常用的命 ...

  6. 45、gridview在改变位置之后无法完整显示的问题记录

    gridview的父布局为layoutFather,gridview id为 layoutGridview layoutFather   高度设置为130dp layoutGridview高度设置为1 ...

  7. C++文件读写之对象的读写

    这里以一个简单的学生信息管理系统为例. 首先是对象的建立,包括姓名,学号,成绩,学分,等 如下: 这里面包括两个子对象, class Student { public: Student() :scor ...

  8. linux命令之grep、cut

    输入: ifconfig eth0 eth0表示主机的第一块网卡. 输出: eth0: flags=<UP,BROADCAST,RUNNING,MULTICAST> mtu inet 19 ...

  9. 团队Alpha版本冲刺(三)

    目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:丹丹 组员7:家伟 组员8:政演 组员9:鸿杰 组员10:刘一好 组员11:何宇恒 展示组内最 ...

  10. 201621123034 《Java程序设计》第2周学习总结

    1. 本周学习总结 本周学习了基本数据类型.包装类,自动装箱与自动拆箱.数组.ArrayList.包装类可以更加方便的转换基本数据类型,而其存放的是对象的引用,而非对象本身,在对其内容进行比较时,要使 ...