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. 如果一个变量根本不存在会 ...
随机推荐
- (ADO.NET)关于C#中“配置”sqlite问题
配置打引号,只是因为觉得只是一些小问题,在此记录一下,第一次遇到还真有点手足无措,昨天到今天~终于可以开始放肆的写sqlite了. 好,第一个问题是引用已下载的system.data.sqlite.d ...
- mac terminal基本命令
文件目录 首先要清楚几个文件目录: " / " :根目录 " ~ " :用户主目录的缩写.例如当前用户为esther,那么" ~ "展开来 ...
- Android开发——用户在屏幕上的手势识别
个定点决定.四个属性分别为left(1),top(2),right(3),bottom(4). 数字为图上标出的距离.显然这四个属性是相对于父容器来定的,均可以通过get()方法获取. 因此很容易得出 ...
- Django基于Pycharm开发之三[LANGUAGE_CODE与TIME_ZONE]
在django/conf/global_settings.py 中,我们可以找到关于language和timezone的通用配置信息,源码如下: # Local time zone for this ...
- luogu3375 【模板】KMP字符串匹配
nxt数组下标从1开始而a,b数组下标从0开始. #include <iostream> #include <cstring> #include <cstdio> ...
- IOS开发---菜鸟学习之路--(十五)-如何实现拍照功能
本章将来讲解下如何实现拍照的功能 我们需要的实现的效果是 好了 直接开始内容吧 首先我们需要新建一个ViewController 就叫AddPictureViewController 然后选 ...
- nsfwjs鉴黄识别最小化案例
3个月前,也就是2月份左右吧,Github上出现一个开源项目: Infinite Red, Inc.工作室宣布开源旗下基于tensorflow的tfjs的鉴黄小工具 据说是从15000张图片中 进行机 ...
- jmeter快捷键
快捷键 功能 备注 Ctrl + C 复制 可复制组件 Ctrl + V 粘贴 可粘贴组件 Ctrl + Shift + C 复制粘贴当前组件到下一行 Ctrl + R 运行测试计划 Ctrl ...
- 用python介绍4种常用的单链表翻转的方法
这里给出了4种4种常用的单链表翻转的方法,分别是: 开辟辅助数组,新建表头反转,就地反转,递归反转 # -*- coding: utf-8 -*- ''' 链表逆序 ''' class ListNod ...
- sqlserver操作各种文件方法
******* 导出到excelEXEC master..xp_cmdshell ''bcp SettleDB.dbo.shanghu out c:\temp1.xls -c -q -S"G ...