在数据库中存在一种特殊的值: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. Django--源码安装

    1.安装setuptools cd /usr/src tar zxf setuptools-18.3.2.tar.gz cd setuptools-18.3.2/ python setup.py bu ...

  2. 如何提高STM32的学习效率

    时间如何安排 做任何事情前,习惯写一个计划——要在一个月内上手STM32! 没有计划的日子,每天早上醒来睁开眼睛,却不知道自己今天要干啥 计划和时间安排: 第一阶段:找感觉——谈及STM32,立即反应 ...

  3. 商品评分效果JavaScript

    <script> window.onload=function(){ //----------选中的星星会多出一个属性:isClick="true" 藉此来获取评分-- ...

  4. msconfig.exe

    msconfig.exe 编辑 本词条缺少概述.名片图,补充相关内容使词条更完整,还能快速升级,赶紧来编辑吧!   中文名 微软系统配置实用程序 外文名 msconfig.exe 出品者 Micros ...

  5. IOS开发---菜鸟学习之路--(二十三)-直接利用键值对的方式来处理数据的感想

    首先声明,本文纯粹只是做为本人个人新手的理解.文中的想法我知道肯定有很多地方是错的. 但是这就是我作为一个新人的使用方法,对于大牛非常欢迎指导,对于喷子请绕道而行. 由于这是早上跟我学长讨论数据处理时 ...

  6. vue知识

    https://juejin.im/post/5af16a2cf265da0b8636353b

  7. Linux下librtmp使用及编程实战

    最近想做rtmp的推流.直播的小项目,不想直接使用FFmpeg进行推流,FFmpeg进行推流特别简单,因为它已经将编码以及librtmp都集成好了,没啥意思.FFmpeg推流的例子,在雷神的博客里可以 ...

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

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

  9. libcmt.lib和msvcrt.lib冲突,原因和解决方法

    libcmt.lib和msvcrt.lib冲突,原因和解决方法 https://blog.csdn.net/longlijun/article/details/7331093 libcmt.lib是w ...

  10. 剑指offer42:翻转单词顺序 VS 左旋转字符串(更高效、简便的解法)

    题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变.为简单起见,标点符号和普通字母一样处理.例如输入字符串"I am a student." ,则输出" ...