NULL的学问
在数据库中存在一种特殊的值: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的学问的更多相关文章
- 【程序员的SQL金典】笔记(第6章~第11章)
第六章 索引与约束 1.索引用来提高数据的检索速度,而约束则用来保证数据的完整性. 2.创建索引 创建索引的SQL 语句是CREATE INDEX,其语法如下: CREATE INDE ...
- SQL金典
ps:补充自己的基础知识,大神请无视.. ~~~~~~~~~~~~~~~~~~~~~ DataBase Management System,DBMS.... Catalog ...库 Table... ...
- 《深入理解JAVA虚拟机》笔记1
java程序运行时的内存空间,按照虚拟机规范有下面几项: )程序计数器 指示下条命令执行地址.当然是线程私有,不然线程怎么能并行的起来. 不重要,占内存很小,忽略不计. )方法区 这个名字很让我迷惑. ...
- 学问Chat UI(3)
前言 上文学问Chat UI(2)分析了消息适配器的实现; 本文主要学习下插件功能如何实现的.并以图片插件功能作为例子详细说明,分析从具体代码入手; 概要 分析策略说明 "+"功能 ...
- 【小计】新人Tostring前忘记Null判断的处理
ToString和string.Concat(可屏蔽Null的异常)性能相差不大,一些中小项目完全可以用Concat(新人容易忘记判断Null的情况,遇到太多了,所以建议重写tostring方法,内部 ...
- 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. ...
- 异步 HttpContext.Current 为空null 另一种解决方法
1.场景 在导入通讯录过程中,把导入的失败.成功的号码数进行统计,然后保存到session中,客户端通过轮询显示状态. 在实现过程中,使用的async调用方法,出现HttpContext.Curren ...
- js中的null 和undefined
参考链接:http://blog.csdn.net/qq_26676207/article/details/53100912 http://www.ruanyifeng.com/blog/2014/0 ...
- JavaScript中undefined与null的区别
通常情况下, 当我们试图访问某个不存在的或者没有赋值的变量时,就会得到一个undefined值.Javascript会自动将声明是没有进行初始化的变量设为undifined. 如果一个变量根本不存在会 ...
随机推荐
- MapReduce进行数据查询和实现推简单荐系统
1 运行环境说明 1.1 硬软件环境 1.2 机器网络环境 2 书面作业1:计算员工相关 2.1 书面作业1内容 2.2 实现过程 2.2.1 准备测试数据 2.2.2 问题1:求各个部 ...
- 第三模块 面向对象& 网络编程基础 实战考核
1.简述构造方法和析构方法. 构造方法(__init__):主要作用是实例化时给实例一些初始化参数,或执行一些其它的初始化工作,总之因为这个__init__只要一实例化, 就会自动执行,不管你在这个方 ...
- win8 远程桌面时提示凭证不工作问题的终极解决办法
环境说明 远程办公电脑(放置于公司.自用办公电脑.win8系统) 远程连接客户机(放置于家中.家庭日常所用.win8系统) 故障现象 最近在使用远程桌面连接公司的办公电脑时,突然发现win8系统总是无 ...
- 设计模式之第13章-职责链模式(Java实现)
设计模式之第13章-职责链模式(Java实现) “请假都那么麻烦,至于么.”“咋的了?”“这不快过年了么,所以我想早两天回去,准备一下,买买东西什么的,然后去给项目经理请假,但是他说快过年了,所以这个 ...
- 56、使用android studio(v1.3.*)修改包名 (rename package name)
一.修改包名 ①选中目录,开始构造 在弹窗中选中Rename directory 在弹窗中选中Rename package 填写新的包名,点击Refactor 如果有警告,不用管它,直接点击Do Re ...
- LeetCode with Python -> Linked List
21. Merge Two Sorted Lists Merge two sorted linked lists and return it as a new list. The new list s ...
- Linux 程序编译过程的来龙去脉
大家肯定都知道计算机程序设计语言通常分为机器语言.汇编语言和高级语言三类.高级语言需要通过翻译成机器语言才能执行,而翻译的方式分为两种,一种是编译型,另一种是解释型,因此我们基本上将高级语言分为两大类 ...
- zabbix3.4 源码部署
centos6.8 系统 zabbix3.4.9 msyql5.7.22 php5.6.24 在centos6上面源码部署zabbix.3.4 问题比较多,需要花不少时间去解决,建议 ...
- 关于微信小程序post请求数据的坑
在post请求数据的时候,发现数据没有发送给后台,需要在请求头里加"Content-Type": "application/x-www-form-urlencoded&q ...
- hdu 4293 区间DP
/* 题目大意:n个人分成若干组,每个人都描叙他们组前面有多少人后面有多少人, 求说真话的人最多有多少个. 解题思路:把同一组的人数统计起来他们组前面有x人后面有y人, num[x+1][n-y]表示 ...