在SQL and Relational Theory(C.J. Date,2009)第4章中,主张避免重复行,并避免在我们存储的数据中使用NULL
属性。虽然我没有避免重复行的麻烦,但我很难看到如何在不使用NULL
的情况下对数据进行建模。例如,以下是 - 这有点不起作用。
我们有一个artist
表,其中包含gender
列。这是gender
表的外键。然而,对于一些艺术家来说,我们并不知道他们的性别 - 例如,我们已经获得了一个没有艺术家描述的新音乐列表。如何在不使用NULL
的情况下表示这些数据?我看到的唯一解决方案是在gender
表中添加一个新的性别“未知”。
虽然我非常喜欢这本书,但在本章结束时我真的很失望:
当然,如果禁止空值,则必须通过其他方式处理缺失的信息。不幸的是,这些其他方法太复杂了,不能在这里详细讨论。
这真是一种耻辱 - 因为这是我等待阅读的解决方案!有一篇参考资料来阅读有很多出版物要阅读的附录,但在我潜入阅读这些内容之前,我希望能有更多的实际总结。
我得到一些人评论他们不明白我为什么要避免'NULL'所以我会再次引用这本书。请进行以下查询:
SELECT s.sno, p.pno
FROM s, p
WHERE s.city <> p.city
OR p.city <> 'Paris'
现在,以s.city为伦敦为例,p.city为Paris。在这种情况下,伦敦&lt;&gt;巴黎,所以查询是真的。现在假设p.city不是Paris,并且实际上是 xyz 。在这种情况下,(伦敦&lt;&gt; xyz )或( xyz &lt;&gt; Paris)也是True。所以,给定任何数据 - 这个查询都是真的。但是,如果xyz为“NULL”,则场景会发生变化。在这种情况下,这两个表达式都不是True 也不是 False,它们实际上是Unknown。在这种情况下,因为结果未知,您将不获取任何返回的行。
从2值逻辑转换到3值逻辑很容易引入这样的错误。事实上,我刚刚在工作中介绍了一个激励这篇文章的内容。我希望所有行都在type != 0
但是,这实际上最终会匹配type == 0 OR type IS NULL
- 令人困惑的行为。
我是否在未来使用NULL
建模我的数据尚不清楚,但我很好奇其他解决方案是什么。 (我也一直认为,如果你不知道,你应该使用NULL
)。
答案 0 :(得分:43)
每个人都在说话,除了dportas和Walter之外,没有人能够理解这个问题。好吧,所以有95%的SO人不理解Null问题,并且因为他们的数据库充满了Null而感到受到威胁,他们想要转换寻求者。无价。当他们争论时,有人会如何学习?
对你好,消除空虚。我从来没有在我的任何数据库中允许Null。
当然,如果禁止空值,则必须通过其他方式处理缺失的信息。不幸的是,这些其他方法太复杂了,不能在这里详细讨论。
实际上它并不是那么难。有三种选择。
1.1。第六范式就是答案。但您无需将整个数据库规范化为6NF。对于每个可选的列,您需要一个远离主表的子表,只需要PK,它也是FK,因为它是1 :: 0-1关系。除PK外,唯一的列是可选列。
看看这个Data Model;第4页的AssetSerial
是一个经典案例:并非所有Assets
都有SerialNumbers
;但是当他们这样做时,我希望他们存储它们;更重要的是我想确保它们是独一无二的。
(对于OO人来说,顺便说一下,这是一个关系表示法的三级类图,一个“Concwete Table Inheritance”,没什么大不了的,我们已经有30年了。)
1.2。对于每个这样的表,使用View来提供表的5NF形式。当然,使用Null(或任何适合该列的值)来标识任何行的列的缺失。但是不要通过视图进行更新。
1.3不要使用直接连接来抓住6NF色谱柱。不要使用外部联接(并且让服务器为缺少的行填充Null)。使用子查询填充列,并指定要为缺失值返回的值(除非您有Oracle,因为其子查询处理甚至比其设置处理更差)。例如。只是一个例如。您可以将数字列转换为字符串,并对缺失的行使用“Missing”。
如果你不想走那么远(6NF),你还有两个选择
。
2.您可以使用Null替代品。我使用CHAR(0)表示字符colomns,0表示数字。但我不允许FK这样做。显然,您需要一个超出正常数据范围的值。这不允许三值逻辑
。
3.除了(2)之外,对于每个Nullable列,您需要一个布尔指示符。对于Sex
列的示例,指标类似于SexIsMissing
或SexLess
(抱歉)。这允许非常紧凑的三值逻辑。 5%的人中有很多人喜欢它,因为数据库仍然是5NF(并且表格较少);缺少信息的列加载了从未使用过的值;它们仅在指标为假时使用。如果您有一个企业数据库,则可以将其包装在一个函数中,并始终使用UDF,而不是原始列。
当然,在所有情况下,您都永远无法编写处理缺失信息所需的代码。无论是ISNULL()
,还是6NF列的子查询,还是在使用该值或UDF之前要检查的指标。
如果Null具有特定含义...... 那么它不是空的!根据定义,Null是未知值。
答案 1 :(得分:19)
那么你如何设计没有NULLS?这是最初的问题。
实际上很容易。你设计的是,每当你不得不丢失一些数据时,你可以通过丢失整行来实现。如果没有行,则不是一行满NULL。它显然不存在。
因此,在“DateOfDeath”的情况下,我们有一个包含两列的表,即PersonId和DateOfDeath。 PersonId在Persons表中引用Id。如果没有要存储的DateOfDeath,我们不存储该行。讨论结束。
如果你在这个和Persons表之间进行OUTER JOIN,那么只要没有行,你就会得到DateOfDeath的NULL。如果你在where子句中使用它,你会得到关于3值逻辑的常见的令人困惑的行为。如果执行INNER JOIN,那么没有DateOfDeath的行将从连接中消失。
说了这么多,我经常在非关键列中允许NULL。我没有简明扼要的方式告诉你我如何确定一个列是关键的。
答案 2 :(得分:6)
非常简单地仅存储已知信息 - 换言之,即封闭世界假设。目标是至少参加博伊斯·科德/第五范式,你不会错误。
答案 3 :(得分:4)
null
是理论符合现实的结果,必须进行调整才能使用。在我看来,试图避免所有null
值最终会导致更糟糕和更难维护的代码,而不仅仅是在适当情况下使用null
。
答案 4 :(得分:3)
需要NULL - 不需要替换它们
NULL的enitre定义是它的未知 - 只需用任意类型替换它就是做同样的事情,为什么呢?
以下评论:
试过这个 - 两者都没有:
declare @x char
set @x = null
if @x = @x
begin
select 'true'
end
if @x <> @x
begin
select 'false'
end
我只能认为这意味着因为null是未知的,所以不能说它等于或不等于 - 因此两个语句都是假的
答案 5 :(得分:1)
NULL
:
A )您有业务原因。例如,在付款表格中,NULL
付款值意味着从未付款。 0.00
付款金额意味着我们故意不付款。对于医疗图表,血压读数的NULL
值意味着您没有服用血压,0
值意味着患者已经死亡。这是重要区别,在某些应用程序中是必需的。
B )您的查询会对此进行说明。如果您了解NULL
对IN
,EXISTS
,不等运算符(如您在OP中指定的那样)等的影响,那么它应该不是问题。如果您的表中现在有NULL
并且不想要某些应用程序的值,则可以使用视图COALESCE
或ISNULL
来填充不同的值,如果源表具有NULL
。
修改强>
使用NULL
解决OP关于“现实世界”不平等/平等的问题,这是我有时使用的一个很好的例子。
你和另外3个人一起参加派对。你知道有一个人被命名为“约翰”但却不知道其他人。
逻辑,“有多少人被命名为Joe”的答案未知或NULL
。在SQL中,这将类似于
SELECT name FROM party where NAME = 'Joe'
你不会得到任何行,因为你不知道他们的名字。他们可能是也可能不是乔。
你的不平等将是:
SELECT name from party where NAME <> 'Joe'
你只能得到“约翰”的回报值,因为约翰的名字就是你所知道的。其他人可能是也可能不是乔,但你无从知晓。
答案 6 :(得分:0)
我不同意作者,并声称NULL实际上是处理可选字段的缺失数据的正确方法。事实上,这就是NULL存在的原因......
针对您有关性别的具体问题:
答案 7 :(得分:0)
如果可能的话,不允许将列定义为NULL。对我而言,它与你想要的业务规则没有任何关系,这意味着它与磁盘I \ O有关。
在SQL Server中,可以为空的列(比如字符10)在空位时占位图中的一位,不可空时占用10位字节。那么如何让null损坏磁盘I / O.它的痛苦方式是将值插入到曾经为null的列中。由于SQL没有保留空间,因此行中没有空间来放置值,因此SQL Server必须移动数据以腾出空间。页面拆分,碎片,更新RID(如果这是HEAP等)都会损害磁盘I / O.
顺便说一句,如果有性别表,我们可以为“无法确定个人的真正性起源或状态”添加另一行。