Document

查询反模式 - 正视NULL值

一、提出问题

  不可避免地,我们都数据库总有一些字段是没有值的。不管是插入一个不完整的行,还是有些列可以合法地拥有一些无效值。SQL 支持一个特殊的空值,就是NULL。

  在很多时候,NULL值导致我们的程序出现报错的现象,于是很多人就开始拒绝NULL值,想各种各样的方法来避免使用NULL值,但是很遗憾,NULL值恰恰就是满足我们的需要用于表示空值的。

  空值经常存在于我们的数据库当中:

  •   例如一个在职员工的离职时间。
  •   例如一辆电力驱动的车的燃油消耗比。

二、反模式

  很多人对于NULL值感觉到恐惧,原因在于不知道什么时候就会因为一个NULL冒出一个报错。实际上都是由于对NULL值的理解有误引起的。很多人将NULL值当做一个0、False、空字符串来理解。实际上SQL将NULL当做一个特殊的值,并不同于0、false、空字符串。

  实际上对NULL值最好的理解是“不知道”。用“不知道”可以正确理解与NULL值的运算,如"+","-",AND,OR,NOT等。

  我们来看看容易出错的地方。

  首先,我们建一张表如下:

  

  在里面添加几条数据:

  

  注意里面的NULL值。

  我们,来看看如下SQL语句的结果:

  

  以上可以理解为:不知道+10=不知道

  

  留意到上面的数据少了一条,Age为NULL的那一条,原因在于NOT (NULL)的值并不为True也并不为False而是NULL。

  以上的例子还有很多,于是就出现了很多人引用一个普通的值来代替NULL值,例如"无效"、"未知"或者-1。假设我们使用的是-1,虽然我们在使用

SELECT * FROM Person WHERE Age <> -1

  作为查询条件,看起来没什么问题,但是这时候当我们使用SUM、AVG等聚合函数的时候就会导致计算结果出错。

  使用NULL并不是反模式,反模式是将NULL作为一个普通值处理或者使用一个普通的值来取代NULL的作用。

三、解决方案 - 将NULL视为特殊值

  下面先列举一些程序员运用NULL运算时期望得到的结果与实际结果。

  1、在标量表达式中使用NULL

表达式 期望值 实际值 原因
NULL=0 TRUE NULL NULL不是0
NULL=12345 FALSE NULL 未指定值不知道是否等于所给值
NULL<>12345 TRUE NULL 未指定值也不知道一定不等于所给值
NULL+12345 12345 NULL NULL不是0
NULL||'string' 'string' NULL NULL不是空字符串
NULL=NULL TRUE NULL 两个都不知道,鬼知道你等不等
NULL!=NULL FALSE NULL 两个都不知道,贵知道你等不等

  2、在布尔表达式中使用NULL

表达式 期望值 实际值 原因
NULL AND TRUE FALSE NULL NULL不是FALSE
NULL AND FALSE FALSE FALSE FALSE AND 什么都是FALSE
NULL OR FALSE FALSE NULL NULL不是FALSE
NULL OR TRUE TRUE TRUE TRUE OR 什么都是TRUE
NOT (NULL) TRUE NULL NULL 不是FALSE

  3、检索NULL值

  由于=NULL或者不等于NULL操作在对NULL进行比较时都是返回NULL,因此在检索NULL的时候,要用写特别操作:

  IS NULL 和 IS NOT NULL

  对于上面的例子,如果我们希望检索NULL,可以这样写:

  

  4、声明NOT NULL列

  如果NULL会破坏程序结构或者NULL本身就是毫无意义的,那么最好就在定义列时加上NOT NULL约束。让数据库来帮你确保约束的实行比自己写代码可靠得多。

   有人建议为每一列都定义一个DEFAULT值,这样一来当在执行插入操作时,即使省略了某一列,也能获得一个非NULL值。这样的建议也并不是通用的。就假设有一个年龄列,设置了一个Default值为0,那么使用AVG聚合函数的时候结算的是错误的结果。NULL值是不被纳入AVG的计算范畴之内的,而0会被计算。

   

   如对于上表:

  

   而,如果我们将最后两行设置为0,那么AVG的结果将是:

   

   5、动态默认值

  动态默认值这个东西的意思是,当我们的查询碰到一个NULL值的时候,我们希望它返回一个非NULL的默认值,已不至于计算出错。

  比如 姓 + NULL + 名返回的是NULL。

  因此,我们不希望中间返回NULL,而是''空字符串。这样计算才正常。

  大部分数据库都提供了一个COALESCE函数实现这个功能,来看SQLServer中的示例:

   我们将第一行的姓名置NULL

  

posted @ 2017-07-13 14:40  从未被超越  阅读(200)  评论(0编辑  收藏  举报