SQL Server null知多少?

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介: 它和true、false组成谓词的三个逻辑值,代表“未知”。与true和false相比,null最难以令人捉摸,因为它没有明确的值,在不同的场景下,它能代表不同的含义。下文以例子的方式给大家分享下null使用的典型场景及对应的用法。

null是什么?

不知道。我是说,他的意思就是不知道(unknown)。

它和true、false组成谓词的三个逻辑值,代表“未知”。与true和false相比,null最难以令人捉摸,因为它没有明确的值,在不同的场景下,它能代表不同的含义。下文以例子的方式给大家分享下null使用的典型场景及对应的用法。

1.check约束与null

之前在SQL ServerCentral.com上看到一个关于check约束的null问题,

作者创建了一个表,在字段orderstatus上设置了check约束,只能插入指定的value的行,现在插入几行数据,其中有一行的value为null,最后问最终有几行可以插入成功。

原文如下:

I want to ensure that the status column for my Orders table only contains specific values. I decide to use this code:

复制代码
create table Orders
( OrderID int primary key
, OrderTotal MONEY
, OrderStatus VARCHAR(20)
constraint Orders_Status_Code check( OrderStatus in ('ACTIVE', 'INACTIVE', 'TBD'))
);
go
复制代码

Now I want to insert data into the table. I run this batch.

insert Orders select 1, 435.43, 'Active'
insert Orders select 2, 554.66, 'InActive'
insert Orders select 3, 129.12, 'Not Active'
insert Orders select 4, 1228.00, NULL

How many rows are in the table? I am running on a default, SQL Server 2014 instance with US English defaults.

(大家先想想答案,如果没有把握就找个测试环境试一试)

《T-SQL基础教程》中关于check约束与null的描述,著者用了一句言简意赅的口诀“check约束拒绝false,接受true和null”。

在上面的例子中,当orderstatus为‘Avative’和’InActive’时,check约束判断的结果是true,所以会插入成功,当为'Not Active’判断的结果为false,插入不成功,最后当为'Null’时,判断的结果是null,插入成功。

所以,正确答案是3。

 

2.比较运算与null

null一个特殊性在于它无法比较(和计算)。null与任何值的任何比较(和计算)都等于null。(unique约束除外,在unique约束中,null是相等的,同一个字段不允许出现两次null)

比如判断null=null的结果是null,判断null<>null的结果也还是null。下面我以不等于(<>)为例,演示比较运算对null的判断。

我先创建一个表,然后插入多行数据,其中有一行orderstatus的值为null,

复制代码
 
if object_id(N’Orders’) is not null drop table orders
 
create table Orders
( OrderID int primary key
, OrderTotal MONEY
, OrderStatus VARCHAR(20)
);
go
 
 
insert Orders select 1, 435.43, 'Active'
insert Orders select 2, 554.66, 'InActive'
insert Orders select 3, 129.12, 'Not Active'
insert Orders select 4, 1228.00, NULL
 
 
复制代码

现在我执行了一个where orderstatus<>'Active' 的查询,

select * from orders where OrderStatus<>'Active'

大家想想null所在的行会不会在查询结果里面。

在上面的例子中,当orderstatus为'InActive' 和'Not Active' 时,where条件判断的结果为true,但当orderstatus为'null' 时,where OrderStatus<>'Active'等价于where null <>'Active',而null与任何一个值的比较结果还是null,所以where条件判断的结果为null。

在SQL Server中,where筛选的原则是“接受true,拒绝false和null”(《T-SQL基础教程》)。所以orderstatus'InActive' 'Not Active'的行显示在结果集总,而orderstatus为null的行不会出现在结果集中

最终,正确答案是:只会返回两行

image

 

3.Not in与null和Not exists与null

not in和not exists都可以用来判断某个对象的存在与否,在大多数场景下两者可以相互替换,但在遇到null时,因为前者是三值逻辑(true|false|unknow)判断而后者只会返回true或false,因此处理的结果会有很大不同。

为了演示两者的区别,我们还是沿用上文的表,分别使用not in和not exists执行一个查询,找出OrderStatus 不为'Active'和'InActive'的行。

复制代码
 
if object_id(N’Orders’) is not null drop table orders
 
create table Orders
( OrderID int primary key
, OrderTotal MONEY
, OrderStatus VARCHAR(20)
);
go
 
 
insert Orders select 1, 435.43, 'Active'
insert Orders select 2, 554.66, 'InActive'
insert Orders select 3, 129.12, 'Not Active'
insert Orders select 4, 1228.00, NULL
 
复制代码

 

3.1Not In与null

在下面这个查询中,where子句中使用not in来过滤数据,where子句的条件是OrderStatus not in ('Active','InActive'),我们期望结果集中包含orderstatus为'Not Active'、'NULL'这两行的数据。

 
select * from orders where OrderStatus not in ('Active','InActive')

这个查询中,当OrderStatus为null时, 原where子句等价于where null <>'Active' AND  null<>'InActive',这就变成了上文中介绍的比较运算与null的问题。where的判断结果还是null,所以该行不会出现在结果集中。而当OrderStatus为'Not Active'时,满足where筛选的为true的条件,会显示在结果集中。

最终,正确答案是:只有一行。

image

说明:in与null的关系与此同理。

 

3.2Not exists与null

现在我们还是期望结果集中包含orderstatus为'Not Active'、'NULL'这两行的数据,这次用Not exists。

在这个查询中,子查询先求出OrderStatus='Active' or  OrderStatus='InActive的行,然后外部查询用not exists过滤子查询的结果,将剩下的行显示在最终结果集中。

复制代码
SELECT *
FROM orders AS o1
WHERE NOT EXISTS( 
                  SELECT *
                  FROM orders AS o2
                  WHERE o1.OrderStatus = o2.OrderStatus
                    AND ( o2.OrderStatus = 'Active'
                       OR o2.OrderStatus = 'InActive'
                        ));
复制代码

 

image

 

为了方便理解,我们将子查询改写成自表连接的方式,

select * from orders as o2 where  o1.OrderStatus=o2.OrderStatus and (o2.OrderStatus='Active' or  o2.OrderStatus='InActive' ))

改写成:

复制代码
SELECT *
FROM orders AS o2
     INNER JOIN orders o1 ON o1.OrderStatus = o2.OrderStatus
                         AND ( o2.OrderStatus = 'Active'
                            OR o2.OrderStatus = 'InActive'
                             );
复制代码

返回的结果集为:

image

然后我们再看外层查询,

外部查询期望使用not exists返回orders表中不包含子查询结果集的行,也就是说,只要orders表没有子查询结果集中的行就返回true,否则返回false(只有存在和不存在,没有unknown的说法)。

按照这个逻辑,orderID为3和4的行不在子查询的结果集中,因此not exists判断为true,而orderID为1和2的行已包含在子查询的结果集中,所以not exists判断为false。最后根据where筛选“接受true,拒绝false和null”的原则,最终只有orderID为3和4的行显示在结果集中。

image

说明:exists与null的关系与此同理。

 

3.3Not  in和Not exists的区别

not in实际上是对一个对象的比较运算,而比较存在true|false|unknow三种逻辑值。

not exsits判断某个对象存在或者不存在,它只有这两种状态,没有unknown的说法。因此相比not in而言,not exists只会有true和false这两种逻辑值。

 

总结:

上文介绍了null在不同场景中的含义,考虑到SQL不同的语言元素对null的不同处理方式,平常我们在写SQL语句的时候应该清晰思考自己编写的每个查询对null或三值逻辑的处理,避免出现逻辑错误。

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
18天前
|
SQL 存储 数据库
SQL NOT NULL
【11月更文挑战第14天】
31 6
|
3月前
|
关系型数据库 MySQL 网络安全
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
|
5月前
|
SQL 存储 监控
SQL Server的并行实施如何优化?
【7月更文挑战第23天】SQL Server的并行实施如何优化?
116 13
|
5月前
|
SQL
解锁 SQL Server 2022的时间序列数据功能
【7月更文挑战第14天】要解锁SQL Server 2022的时间序列数据功能,可使用`generate_series`函数生成整数序列,例如:`SELECT value FROM generate_series(1, 10)。此外,`date_bucket`函数能按指定间隔(如周)对日期时间值分组,这些工具结合窗口函数和其他时间日期函数,能高效处理和分析时间序列数据。更多信息请参考官方文档和技术资料。
|
5月前
|
SQL 存储 网络安全
关系数据库SQLserver 安装 SQL Server
【7月更文挑战第26天】
63 6
|
5月前
|
SQL
SQL NOT NULL 约束
【7月更文挑战第18天】SQL NOT NULL 约束。
46 6
|
5月前
|
存储 SQL C++
对比 SQL Server中的VARCHAR(max) 与VARCHAR(n) 数据类型
【7月更文挑战7天】SQL Server 中的 VARCHAR(max) vs VARCHAR(n): - VARCHAR(n) 存储最多 n 个字符(1-8000),适合短文本。 - VARCHAR(max) 可存储约 21 亿个字符,适合大量文本。 - VARCHAR(n) 在处理小数据时性能更好,空间固定。 - VARCHAR(max) 对于大文本更合适,但可能影响性能。 - 选择取决于数据长度预期和业务需求。
400 1
|
4月前
|
SQL 安全 Java
驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。错误:“The server selected protocol version TLS10 is not accepted by client
驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。错误:“The server selected protocol version TLS10 is not accepted by client
505 0
|
5月前
|
SQL 存储 索引
MySQL设计规约问题之为什么应该把字段定义为NOT NULL并且提供默认值
MySQL设计规约问题之为什么应该把字段定义为NOT NULL并且提供默认值
|
5月前
|
SQL 监控 数据库
SQL Server 查询超时问题排查
【7月更文挑战第8天】排查 SQL Server 查询超时涉及五个主要方面:检查复杂查询、评估服务器性能、审视配置参数、更新统计信息和分析执行计划。关注点包括查询的结构(如连接、子查询和索引),服务器资源(CPU、内存、网络延迟),连接和内存设置,以及统计信息的时效性。通过这些步骤可定位并解决性能瓶颈。
128 0