一、提出问题
不可避免地,我们都数据库总有一些字段是没有值的。不管是插入一个不完整的行,还是有些列可以合法地拥有一些无效值。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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现