在数据库中存在一种特殊的值: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. 4152: [AMPPZ2014]The Captain

    4152: [AMPPZ2014]The Captain Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1561  Solved: 620[Submi ...

  2. U10783 名字被和谐了

    U10783 名字被和谐了 题目背景 众所周知,我们称g是a的约数,当且仅当g是正数且a mod g = 0. 众所周知,若g既是a的约数也是b的约数,我们称g是a.b的一个公约数. 众所周知,a.b ...

  3. 数据预处理之独热编码(One-Hot Encoding)

    问题的由来 在很多机器学习任务中,特征并不总是连续值,而有可能是分类值. 例如,考虑以下三个特征: ["male","female"] ["from ...

  4. redis 之相关命令

    为什么缓存数据库更要首选redis?如何使用redis? 一.使用缓存数据库为什么首选用redis? 我们都知道,把一些热数据存到缓存中可以极大的提高速度,那么问题来了,是用Redis好还是Memca ...

  5. loj2061 「HAOI2016」放棋子

    答案就是错排数 n = int(input()) f = [0] * 205 f[0] = 1 for i in range(2, n+1): f[i] = (i-1) * (f[i-1] + f[i ...

  6. Python-S9-Day122-Python爬虫

    11 初识爬虫 11 初识爬虫 12 今日作业 11.1 初识黄页: 11.2 互联网就是一张大的蜘蛛网: 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种 ...

  7. 用python介绍4种常用的单链表翻转的方法

    这里给出了4种4种常用的单链表翻转的方法,分别是: 开辟辅助数组,新建表头反转,就地反转,递归反转 # -*- coding: utf-8 -*- ''' 链表逆序 ''' class ListNod ...

  8. python中 in, any 和 all用法

    in if x == 1 or y == 1 or z == 1: print('passed') if 1 in (x, y, z): print('passed') any if x or y o ...

  9. android AsyncTask使用限制

    由于AsyncTask内部是使用线程池(ThreadPoolExecutor)来管理要处理的任务的,所以AsyncTask的弊端就非常明确了:要extcute的任务数量超过线程池最大容量时,必然会报错 ...

  10. Windows下Git多账号ssh-key(复制自己用)

    Windows下Git多账号配置,同一电脑多个ssh-key的管理 这一篇文章是对上一篇文章<Git-TortoiseGit完整配置流程>的拓展,所以需要对上一篇文章有所了解,当然直接往下 ...