数据库中空值问题的讨论

时间:2022-01-19 00:28:16

长久以来,不同的数据库设计人员在设计表字段时,有的设计字段允许为NULL,有的设置了默认值(不允许字段中出现NULL值)。对于字段允不允许为空的争论,由来已久,但个人的设计原则是不允许出现NULL。下面从各方面来加以讨论和描述:

1.         处理性能:SQLSERVERNULL的处理上比空字符串处理复杂,它需要在每一行用额外的空间来标记某一字段是NULL,查找数据的时候会检查这个标志,然后根据这个标志返回相应的NULL给用户,所以处理上性能有所损耗,当然在现在的配置机器,应该可以忽略这些时间。根据个人的测试,先创建一个表,表结构如下:

create table testtable

(

  intfield int not null

    primary key(intfield), -- 1开始递增

  chrfield uniqueidentifier, -- GUID

  vchfield varchar(40),  -- 上面字段的字符串形式

  dtefield datetime,     -- 日期时间

  in2field int           -- 对应intfield 的值

)

                     然后插入500万条数据,在插入时,所有列都有值,用如下语句进行查询:

                            declare @date datetime

set @date = getdate()

select * from testtable

where  in2field >= 4999990

select getdate() - @date

然后设置部分行的in2field字段为NULL,再用上面的查询语句查询,对比二种情况下的所用时间,在没有NULL的情况下占用时间较小。

2.         索引问题:如果用了NULL,索引就会无效,变成全表检索,影响处理速度。同时,如果一个单个的列中有不止一行包含 NULL,则无法在该列上创建唯一索引(当然,用默认值的情况下,如果列中有多个默认值,也不能创建唯一索引)。

3.         编码复杂度:对有空值出现的列处理和判断时,需要增加额外的代码量,不管是在SQL中还是在各种前台语言中,复杂度远远超过检查空字符串及一些数字(比如0)来的慢,使用不慎还会引起错误。如:在有空值参与计算的表达式中,无论表达式如何计算,表达式返回的永远是NULL,特别是对于不太熟悉的开发人员,往往会写出如下代码:

              select (intfield + in2field) as sumfield from testable where   intfield = @intfield

上面表中,intfield 列有空值出现的情况。有的初学者,甚至会写出如下的语句:

       select * from testable where intfield = NULL

有的数据库有配置项来决定NULL参与计算时的规则,但个人认为,不要依赖于数据库的特殊功能来帮助处理业务逻辑。

4.         对象映射问题:在对象模型中,一个固定的对象有N个固定的属性,一般情况下,根据所有的属性的组合,我们可以寻找到指定的对象,如果对象的属性发生变化,并不影响该对象本身,但如果属性有了增加或减少,就会改变对象的类。在数据库中的关系映射到Object也是一样,空值属性在某些情况会被认同为该属性并不存在(虽然它会突然又有了,但这应该作为该对象的状态而不是对象出现),这十分不利于关系与Object的映射。在java中,有O/R可以对象和关系进行转换,.net中虽然目前没有相应的并很成熟的工具,但我觉得,采取OO的思想仍是必要的,毕竟,对事物的描述是一个复杂的过程,尤其是在开发大型的数据库上,最现实的问题就是,业务逻辑的规则,将严重地影响编程效率。

5.         集合函数计数问题:大多数集合函数都能在计算时消除空值;COUNT函数则属于例外。对包含空值的一个列使用COUNT函数,空值会从计算中消除。但假如COUNT函数使用一个星号,它就计算所有行,而不管是否存在空值。

6.         空间占用量:NULL不会占用空间,但如果用默认值,所占空间也不会多很多。

7.         规范性:统一所有字段为“不允许空”并设置“缺省值”。这是一种“规范”。空值的问题,应该由数据库本身设计时就解决掉,而不应该在应用程序的数据访问层中进行处理。