查询反模式 - 正视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=0TRUENULLNULL不是0
NULL=12345FALSENULL未指定值不知道是否等于所给值
NULL<>12345TRUENULL未指定值也不知道一定不等于所给值
NULL+1234512345NULLNULL不是0
NULL||'string''string'NULLNULL不是空字符串
NULL=NULLTRUENULL两个都不知道,鬼知道你等不等
NULL!=NULLFALSENULL两个都不知道,贵知道你等不等

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

表达式期望值实际值原因
NULL AND TRUEFALSENULLNULL不是FALSE
NULL AND FALSEFALSEFALSEFALSE AND 什么都是FALSE
NULL OR FALSEFALSENULLNULL不是FALSE
NULL OR TRUETRUETRUETRUE OR 什么都是TRUE
NOT (NULL)TRUENULLNULL 不是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

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值