在数据库中存在一种特殊的值: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. hadoop: Shuffle过程详解 (转载)

    原文地址:http://langyu.iteye.com/blog/992916 另一篇博文:http://www.cnblogs.com/gwgyk/p/3997849.html Shuffle过程 ...

  2. 设计模式之第12章-享元模式(Java实现)

    设计模式之第12章-享元模式(Java实现) “怎么回事,竟然出现了OutOfMemory的错误.鱼哥,来帮我看看啊.”“有跟踪错误原因么?是内存泄露么?”“不是内存泄露啊,具体原因不知道啊.对了,有 ...

  3. IOS开发---菜鸟学习之路--(二十)-二维码扫描功能的实现

    本章将讲解如何实现二维码扫描的功能 首先在github上下载ZBar SDK地址https://github.com/bmorton/ZBarSDK 然后将如下的相关类库添加进去 AVFoundati ...

  4. IOS开发学习笔记041-UITableView总结1

    一.UITableView的常用属性 1.分割线 // 分割线 self.tableView.separatorColor = [UIColorredColor]; // 隐藏分割线 self.tab ...

  5. Leetcode 611.有效三角形的个数

    有效三角形的个数 给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数. 示例 1: 输入: [2,2,3,4] 输出: 3 解释: 有效的组合是: 2,3,4 (使用第一个 ...

  6. 单元测试-mock基础

    本文较短,只是备份一下mock的几个常用基础例子方便复习 目录 介绍mock的使用例子 maven资源 <dependency> <groupId>org.mockito< ...

  7. hdu 4176

    Class Statistics Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  8. git 远程仓库回滚

    git branch backup #创建备份分支 git push origin backup:backup #push到远程 git reset --hard commit_id #本地分支回滚 ...

  9. 【距离GDOI:136天】 后缀数组中...

    当时后缀数组没有好好学...各种应用都没学,这两天好好补,要把罗神的论文好好研究一遍...其实后缀数组真的好神奇!!特别是那个萌萌的height数组! 今天终于能有两节完整的晚自修了QAQ...明晚还 ...

  10. linux系统——etc下的profile文件

    /etc/profile文件 /etc/profile是全局的,适用于所有的shell.在刚登录Linux时,首先启动 /etc/profile 文件. profile文件会告诉shell使用什么语言 ...