可空的外键不好的做法?


114

假设您有一个带有客户ID外键的订单表。现在,假设您要添加一个没有客户ID的订单,(是否应该是另一个问题),您将必须使外键为NULL ...是一种不好的做法,还是宁愿使用之间的链接表订单和客户?尽管关系为1到n,但链接表会将其设为n到n。另一方面,有了链接表,我不再有那些NULL了...

实际上,数据库中不会有很多NULL,因为带有外键NULL的记录只是暂时的,直到添加订单的客户为止。

(在我的情况下,它不是订单和客户)。

编辑:链接到未分配的客户呢?


9
这是在数据库模式中提供NULL的主要目的之一。此外,这就是为什么您可以声明字段NULL或NOT NULL的原因,从而可以满足架构的特定要求。
gahooa

7
我最初将问题读为可为空的键,并准备提出一些有力建议... :-)
Andrzej Doyle,2009年

Answers:


51

拥有链接表可能是一个更好的选择。至少它不违反规范化BCNF(Boyce-Codd规范形式)。但是我更愿意务实。如果这些空值很少,并且它们只是临时的,我认为您应该跳过链接表,因为它只会增加方案的复杂性。

附带说明;使用链接表不一定使它成为n到n,如果您在链接表中使用指向订单表的外键作为该链接表中的主键,则该关系仍然为1..n。每个订单在该链接表中只能有一个条目。


2
source__destination_link或SourceDestination
Svisstack 2012年

7
我有兴趣了解链接表更好的情况,但我从未遇到过以任何方式都会改善流程的情况。
Reimius 2012年

5
正如我在回答中指出的那样,在这种情况下,我会务实,不使用链接表。我确信普通表格不是为了改善流程而发明的,而是为了确保一致性并避免重复。不过,这是一个非常笼统的讨论,我认为必须根据具体情况进行讨论。
PatrikHägne2012年

110

否可空FK没错。当FK指向的实体与主键引用表之间的关系为(零或一个)到(1或很多)时,这是常见的。

例如,如果您在一个表中同时具有物理地址和邮件地址属性(列),并且FK指向地址表。当实体只有一个邮政信箱(邮寄地址)时,您可以使“物理地址”为空,以便处理,而当邮件地址与“物理地址”相同(或不与)相同时,您可以使邮件地址为空。


38

根据我所读的内容,可空列可以是1NF到5NF,但不能是6NF。

只有当您比Chris Date更好地了解“首个范式真正意味着什么”时。如果x和y都可以为空,并且实际上在某些行中x和y都可以null,则WHERE x=y不会产生true。毫无疑问,这证明null不是值(因为任何实际值始终等于其自身)。而且由于RM规定“表的每个单元格中都必须有一个值”,所以任何可能包含空值的事物都不是关系事物,因此甚至不会出现1NF问题。

我听说它认为Nullable列通常会破坏第一个标准化程度。

请参见上文,了解该论点的合理原因。

但实际上,这是非常实用的。

仅当您可以避免在世界其他地方通常会引起的头痛时才可以免疫。一个这样的头痛(相对于其他null现象,这只是一个小问题),它WHERE x=y在SQL中实际上意味着WHERE x is not null and y is not null and x=y,但大多数程序员根本不了解这一事实,只是阅读了一下。有时没有任何伤害,其他时候则没有。

实际上,可为空的列违反了最基本的数据库设计规则之一:不要在一个列中组合不同的信息元素。空值之所以如此,是因为它们将布尔值“此字段确实存在/不存在”与实际值结合在一起。


17
对于“其中x不为null且y不为null且x = y”的+1。没意识到。
RobM 2011年

1
很好地列出了论点和示例。
pedz 2014年

1
一个问题。当值“不存在”(这是现实情况)且数据库属性不允许为空时,该属性中的任何值均为WRONG。关于头痛,请记住,KISS,这不仅意味着保持简单,还意味着保持尽可能简单,但并非简单。如果“关系模型”需要不现实,愚蠢的结果,那么可能需要扩展规则以处理实际数据是必需的吗?
查尔斯·布雷塔纳

1
已经表明,三值逻辑导致对四值逻辑的需求,这导致对五值逻辑等的需求。等等。二值逻辑足够了,但是当应用它使“尽可能简单”仍远不如“我们想要的简单”那么简单。
Erwin Smout

2
Chris Date,《逻辑与数据库》,第6章,“为什么关系DBMS逻辑一定不能为多值”,第145页。该章的引用列表也应该很有趣,尤其是涉及McGoveran的引用列表。
Erwin Smout

13

我看不出有什么错,它只是一个可选的n-1关系,将在外键中用null表示。否则,如果您放置链接表,则必须管理它不会成为nn关系,从而造成更多麻烦。


2
实际上,这是0-N关系,而不是可选的1-N关系。但是我同意你的看法。
Eric J.

5
管理?这是0对1方面的唯一UNIQUE约束!
wqw

2
是的,这是一个唯一约束,但是由于该约束,您稍后还必须在代码中处理可能的异常……
pedromarce

4

在关系模型中,可选关系肯定是可能的。

您可以使用null表示不存在关系。它们很方便,但是它们会给您带来头痛,而让您感到头疼的是其他地方。他们不会造成任何麻烦的一个地方就是加入。外键中具有null的行与引用表中的任何行都不匹配。因此,他们退出了内部联接。如果执行外部联接,则无论如何都将处理null。

如果您确实想避免使用null(第6个正常形式),则可以分解表。两个分解表之一具有两个外键列。一个是您具有的可选外键,另一个是引用原始表的主键的外键。现在,您必须使用约束来防止这种关系变成多对多关系,您想避免这种情况。


2

使用NULL是清除不完整订单的好方法:

SELECT * FROM `orders`
WHERE `started_time` < (UNIX_TIMESTAMP() + 900) AND `customer_id` IS NULL

上面显示的是超过15分钟的订单,但没有相关的客户ID。


1

如果您只是暂时添加没有客户ID的订单直到定义了客户,那么在单个交易中添加客户和订单会不会更简单,从而消除了对NULL外键输入的需求并避免了任何约束或触发器你设置被侵犯了吗?

通常,这种情况出现在Web应用程序中,在客户定义其身份之前先详细说明订单。在这种情况下,订单将保持服务器状态或cookie的状态,直到提供了完整订单的所有必要状态为止,此时订单将被持久保存到数据库中。

如上所述,对于地址之类的东西,可以使用NULL外键。但是NULL客户字段对于订单没有意义,应该加以限制。


订单客户就是一个例子。在我的应用中,它的id更像是地址。无法立即找到一个完全正确的示例。谢谢。
Lieven Cardoen

1
如果该数据库用于将商品存储在购物车中,而该购物车不属于注册用户,则这可能是有效的方案。
约翰尼·卡尔

1

您总是可以向客户表中添加一个人造行,例如Id = -1和CustomerName ='Unknown',然后在通常将OrderId中的CustomerId设置为-1的情况下。

这使您没有可为空的FK,但仍然可以适当地表示缺少数据(并将您从不知道如何处理NULL的下游用户中拯救出来)。


只是要补充一点,请记住,NULLS不会存储在索引中(在oracle中),因此这意味着跳过链接表并使用可为空的FK有意义-性能方面。它可能依赖的另一件事是,是否要在此链接表中存储其他内容,例如,世卫组织创建了链接,何时建立链接?链接现在处于非活动状态/已删除(但曾经是吗?)
Worthy7 '16

这是一个坏主意。如果您设置了外键,并且稍后将其指向的数据删除,则不会出现外键异常,现在您的数据变得毫无意义。更糟糕的是,如果以后将其他内容分配给该密钥,则您指向的完全是错误的客户
IcedD​​ante

0

用于可选多对一关系的可空FK完全可以。


-1

我听说它认为Nullable列通常打破了规范化的第一级。但实际上,这是非常实用的。


3
根据我所读的内容,可空列可以是1NF到5NF,但不能是6NF。
Walter Mitty

-1

是的,出了点问题。如果可以为空,则不是外键。其数据库设计由代码组成。也许您将未分配的链接设为零。或“ Unassigned”(如果您使用字符列)。保持数据完整性100%。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.