如何在数据库中避免NULL,同时还表示缺少数据?

och*_*les 29 sql database schema database-design data-modeling

SQL和关系理论(CJ 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'
Run Code Online (Sandbox Code Playgroud)

现在,以s.city为伦敦为例,p.city为Paris.在这种情况下,伦敦<>巴黎,所以查询是真的.现在假设p.city不是Paris,并且实际上是xyz.在这种情况下,(London <> xyz)OR(xyz <> Paris)也是True.所以,给定任何数据 - 这个查询都是真的.但是,如果xyz为"NULL",则场景会发生变化.在这种情况下,这两个表达式既不是True 也不是 False,它们实际上是Unknown.在这种情况下,因为结果未知,您将不会返回任何行.

从2值逻辑到3值逻辑的转变很容易引入这样的错误.事实上,我刚刚在工作中介绍了一个激励这篇文章的内容.我想要所有的行,type != 0然而,这实际上最终匹配type == 0 OR type IS NULL- 令人困惑的行为.

我是否NULL在未来对数据进行建模尚不清楚,但我很好奇其他解决方案是什么.(我也一直认为,如果你不知道,你应该使用NULL).

Per*_*DBA 44

每个人都在说话,除了dportas和Walter之外,没有人能够理解这个问题.好吧,所以有95%的SO人不理解Null问题,并且因为他们的数据库充满了Null而感到受到威胁,他们想要转换寻求者.无价.当他们争论时,有人会如何学习?

为了消除空虚,对你好.我从未在任何数据库中使用Null.

当然,如果禁止空值,则必须通过其他方式处理丢失的信息.不幸的是,那些其他手段太复杂了,不能在这里详细讨论.

实际上它并不那么难.有三种选择.

  1. 这是一篇关于如何处理 H Darwen 不使用NULL信息的论文,这可能有助于解决问题.

1.1.第六范式就是答案.但是您不必将整个数据库规范化为6NF.对于每个可选的列,您需要一个远离主表的子表,只需要PK,它也是FK,因为它是1 :: 0-1关系.除PK外,唯一的列是可选列.

看看这个数据模型 ; AssetSerial在第4页是一个经典案例:并非所有人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列的示例,指标将类似于SexIsMissingSexLess(抱歉).这允许非常紧凑的三值逻辑.5%的人中有很多人喜欢它,因为数据库仍然是5NF(并且表格较少); 缺少信息的列加载了从未使用过的值; 它们仅在指标为假时使用.如果您有一个企业数据库,则可以将其包装在一个Function中,并始终使用UDF,而不是原始列.

当然,在所有情况下,您都永远无法编写处理缺失信息所需的代码.是否ISNULL()为6NF列的子查询,或者在使用该值之前要检查的指示符,还是UDF.

如果Null具有特定含义......那么它就不是空的!根据定义,Null是未知值.

  • 谢谢你的回答!是的,遗憾的是,很多人似乎并没有真正理解我的观点,只是假设我没有“得到”NULL——这根本不是真的;我只是不认为任何事情是最终的方法,并且希望保持我的选择。再次感谢! (2认同)
  • 这种“解决方案”总是让我感觉被欺骗的地方是:“指定您想要为缺失值返回的值”。如果你想要一个“哨兵值”,那么要么你必须在某种类型中保留这样的值(例如`0`、`''`),或者你需要将你的类型与不属于域。这就是 `null` 的初衷。所以你似乎回到了你开始的地方。 (2认同)
  • @IMSoP。呃没有。我没有空值,所以我没有问题。你有空,和问题。你习惯了绕圈子,一遍遍地看起点,所以你以为别人也是这样。我不。无论如何,我不是在回答你的问题。不要使用我的解决方案,使用你的“解决方案”,不要担心另一半如何生活。如果你猜测它们,你肯定会崩溃,并欺骗自己。如果您真的不明白,请提出问题,而不是发表声明。 (2认同)
  • @PerformanceDBA 哇,防御力太强了!我认为(读回我自己的评论),我想说的是*在添加哨兵值时*,您必须在视图/逻辑中重新创建与 NULL 非常相似的东西,只是具有不同的实现细节。例如,用字符串“unknown”填充值,并确保该值在整个应用程序中得到特殊处理。在某些情况下,您实际上需要 3VL(以避免两个不同行中的“未知”评估为相等),因此 AFAICS,这更多是“使用 NULL 更好”而不是“不使用 NULL”的情况。 (2认同)
  • @PerformanceDBA 我不知道你为什么要亲自接受这个。如果我说的话以任何方式冒犯了你,我很抱歉。我并不是说我“感觉被*你*欺骗了”,而是声称“空值是不必要的”,而实际上它似乎归结为“空值可以更好地实现”。我的问题是:当您引入哨兵值时(例如在第 1.3 点或第 2 点中),在某些情况下是否不需要重新实现 3VL,例如回答“薪水 &gt; 5000 美元”这个问题吗?对于带有“salary = 'Unknown'”的项目?如果是这样,这与谨慎使用 NULL 有何不同? (2认同)
  • /sf/ask/464680401/ 的答案表明选项 2:空替代实际上并没有比无效的。 (2认同)

Wal*_*tty 19

那你如何设计没有NULLS?这是最初的问题.

这实际上非常简单.你设计的是,每当你不得不丢失一些数据时,你可以通过丢失整行来实现.如果没有行,则不是一行满NULL.它显然不存在.

因此,在"DateOfDeath"的情况下,我们有一个包含两列的表,即PersonId和DateOfDeath.PersonId在Persons表中引用Id.如果没有要存储的DateOfDeath,我们不存储该行.讨论结束.

如果你在这个和Persons表之间进行OUTER JOIN,那么只要没有行,你就会得到DateOfDeath的NULL.如果你在where子句中使用它,你会得到关于3值逻辑的常见的令人困惑的行为.如果执行INNER JOIN,那么没有DateOfDeath的行将从连接中消失.

允许每列强制为NOT NULL的设计被称为第六范式.

说了这么多,我经常在非关键列中允许NULL.而且我没有简明扼要的方式告诉您如何确定列是否至关重要.

  • 简明扼要,非宗教,实用的建议结束.很好的答案! (2认同)

nvo*_*gel 7

很简单,只存储已知信息 - 换句话说就是封闭世界假设.目标是至少在博伊斯·科德/第五范式,你不会走错路.


Don*_*nie 5

nulls 是理论满足现实的结果,必须进行调整才能使用。在我看来,null与仅null在适当的地方使用相比,试图避免所有值最终会导致更丑陋和更不易维护的代码。

  • 我同意。理论是实用的,但这个真理有其局限性。从理论上讲,理论和实践没有区别。在实践中,有。问题通常来自未能在数据库文档中明确说明,给定的 NULL 实际意味着什么,超出“此处没有数据”。 (2认同)