Constraints在数据含有NULL时的隐忧

NULL在数据库中一直以来常会被拿出来警世,也看过一些书籍或听一些演讲都会提到尽量不要让字段是可以允许NULL的。


常被用来举例的就是NULL无法比较(它既不是0也不是1,而是unknow),因此往往范例都是趋向Where条件式会因为数据含有NULL而造成回传的结果是错误的。
例如以下范例:

Use tempdb
GO
--建一数据表
Create Table tb1(id int identity Primary key,name varchar(10),sex char(1))
GO
--写入数据
Insert Into tb1 values('Rock','M'),('Cary','F'),('John',NULL)
GO
--筛选出性别不是男生的数据
Select * From tb1 Where sex <> 'M'
GO

由结果来看(如下图所示),sex不是男性的数据只有一笔,而sex字段是NULL的John没有被筛选出来。

又或者是当NULL存在于NOT IN的Subquery里也是一样会发生悲剧

Use tempdb
GO
--建一数据表
Create Table tbtype(id int identity Primary key,typecode char(1))
Create Table tbObject(id int identity Primary key,ObjName varchar(50),typecode char(1))
GO
--写入数据
Insert Into tbtype values('A'),('B'),(NULL)
Insert Into tbObject values('Apple','A'),('Asus','B'),('HTC','B'),('MI','C')
GO
--筛选出typecode不在tbtype数据表中的数据
Select * From tbObject Where typecode Not in(Select typecode From tbtype)
GO

如下图红色圈选处,我们发现tbObject中有一笔数据的typecode='C',但是在我们测试中该笔数据并没有被筛出来,反而回传零笔数据回来。这就是当NULL存在NOT IN的Subquery时会发生的潜在问题。

然而以上这些问题都是大家经常拿来检讨该不该有NULL的理由,近日ROCK读了一本电子书(Defensive Database Programming with  SQL Server)发现NULL对于Constraints(条件约束)影响更是不容小歔。以下是简易Demo Code环境建立。

Use Tempdb
GO
CREATE TABLE Boxes(id int identity PRIMARY KEY , Leng int ,Width int ,Height int) ;
GO
--第一个Check验证 高<10 
ALTER TABLE Boxes ADD CONSTRAINT Boxes_Check1 CHECK(Height<10) ;
GO
--第二个Check验证 长>宽>高
ALTER TABLE dbo.Boxes
ADD CONSTRAINT Boxes_Check2 CHECK(Height <= Width AND Width <= Leng) ;
GO

完成上述环境建立后,我们先验证一下我们的Check Constraints是否可以正常运行。下图中我们故意让数据中高度等于10,这样一来就会违背我们的Boxes_Check1条件而发生错误。

接下来我们故意让宽度大于长度并写入数据,如下图所示会因为违背Boxes_Check2条件约束而发生错误。

以上两个简易测试可以知道目前的Check Constraints是正常运行的。

接下来我将含NULL的数据写入数据表看看我们的Check Constrains是不是也挡得住。如下图Insert语法中,我长度给8,宽度是NULL然后高度是9,高度大于长度显然违反Boxes_Check2条件约束。然而该笔数据居然可以顺利写入Table而没被Check Constraints给拦住。

下图中再输入数据的高度字段上我故意给NULL值,我们也可以从结果发现该笔数据也避过Boxes_Check1条件约束而顺利写入到数据表中。

除了刚刚Demo的Constraints会有遇到NULL就无用武之地外,连确保数据完整性的Foreign Key也会因为关联字段值含NULL而破功,以下是简易的Demo。

Use Tempdb
GO
Create Table tbFk1(id1 int not null,id2 int not null,name varchar(10),primary key(id1,id2));
GO
Create Table tbFk2(id int identity Primary Key,id1 int,id2 int,Phone varchar(10));
GO
--为tbFk1及tbFk2建立Foreign Key的关联
Alter Table tbFk2 Add Constraint FK_tbFk1_tbFk2 Foreign Key(id1,id2)
References tbFK1(id1,id2);
GO

完成上面的环境建置后,我们来测试一下Foreign Key是否可以正常发会功效。如下图所示我们在父数据表(tbfk1)中写入一笔id1=1及id2=1的数据,接着我们也可以顺利在子数据表(tbfk2)中写入一笔id1=1及id2=1的数据。

接这我们故意要Insert一笔父数据表不存在的数据到子数据表中,也就是id1=1及id2=2的数据。由下图可以得知这一笔数据写入发生失败,因为违反了Foreign Key的规则。

然而NULL会打破我们的Foreign Key规则,如下图所示不管是id1=null或id2=null,只要包含在关联的数据字段有含NULL值时,我们的Foreign Key就不检查而直接让数据写入(下图红色圈选处)。

经过简易测试,可以发现NULL的雷还真的蛮多的,请大家在开发时务必注意。但以上的问题其实只要我们在相关字段上设定NOT NULL就可以避免掉Constraints没有Check的问题发生了。

我是ROCK

[email protected]

原文:大专栏  Constraints在数据含有NULL时的隐忧


猜你喜欢

转载自www.cnblogs.com/petewell/p/11445564.html