外键可以为NULL和/或重复吗?


325

请为我澄清两件事:

  1. 外键可以为NULL吗?
  2. 外键可以重复吗?

据我所知,NULL不应该在外键中使用,但是在我的某些应用程序中,我可以NULL在Oracle和SQL Server中进行输入,而且我也不知道为什么。


1
@Adrian:据我所知,外键不能为null,但在sql server和oracle中为null。你能解释为什么吗?
jams

@Jams-阅读我答案中的链接。
JNK

11
由于答案和问题很有用,因此无法删除。随时编辑问题以进行改进。
杰夫·阿特伍德

请把关于重复的问题分开。下面仅回答有关NULL的问题。
reinierpost

Answers:


527

简短答案:是,可以为NULL或重复。

我想解释一下为什么外键可能需要为空,或者可能需要唯一或不唯一。首先要记住,外键仅要求该字段中的值必须首先存在于另一个表(父表)中。这就是FK的全部定义。根据定义,Null不是值。Null表示我们尚不知道该值是什么。

让我给你一个真实的例子。假设您有一个存储销售建议的数据库。进一步假设每个提案仅分配了一个销售人员和一个客户。因此,您的提案表将具有两个外键,一个具有客户ID,一个具有销售代表ID。但是,在创建记录时,并不总是分配销售代表(因为尚无人可以自由处理),因此填写了客户ID,但是销售代表ID可能为空。换句话说,当您在输入数据时可能不知道空FK的值,但是您确实知道表中需要输入的其他值时,通常需要具有空FK的能力。要在FK中允许空值,通常要做的就是在具有FK的字段上允许空值。空值与其作为FK的想法是分开的。

它是否唯一与表是否与父表具有一对一或一对多的关系有关。现在,如果您具有一对一关系,则可以将所有数据都放在一个表中,但是如果表太宽或数据涉及不同的主题(员工-保险示例@tbone给例如),那么您想要带有FK的单独表。然后,您可能想使此FK兼作PK(保证唯一性)或对其施加唯一约束。

大多数FK都是一对多的关系,这就是您从FK得到的,而无需在字段上增加其他约束。因此,您有一个订单表和一个订单详细信息表。如果客户一次订购十个项目,则他有一个订单和十个订单明细记录,这些记录包含与FK相同的orderID。


13
因此,这比拥有一个名为“ Unassigned”的假销售人员要好吗?
Thomas Weller 2014年

8
一条评论。空值为不知道SQL(mis)如何处理3VL的人们在查询中留下很多错误的余地。如果某个特定的r-table确实不需要销售人员,则不要包含该记录。一个单独的表可以是“ ProposalAssignedTo”或带有适当约束的某个表。然后,查询编写者可以加入该表,并为提案没有销售员时我们想做的事情提供自己的逻辑。NULL不仅意味着“我们不知道”,它还可以用于很多事情(这就是为什么它几乎总是一个坏主意的原因)
N West

26
@nWest,我不允许不称职的人查询我的数据库,并且任何不知道如何处理null的开发人员也不称职。有时,对于特定字段,在初始数据输入时数据未知,但是那时需要其他字段。
HLGEM

28
@ThomasWeller引用假销售员(“未分配”)会使问题更严重。我假设您的销售员表有多个列...?未分配先生的社会保险号码是多少?他被分配到哪个部门?谁是他的老板?希望您能理解我的观点:创建“未分配”销售员时,您会很快发现自己NULL在一个表中用另一张表中的多个NULL进行了交易。
吉利

1
@ThomasWeller如果/当您需要本地化界面时,也会遇到问题。
tobiv


45

从马口中:

即使没有匹配的PRIMARY或UNIQUE键,外键也允许所有键值为NULL

外键无约束

当在外键上没有定义其他约束时,子表中的任何数量的行都可以引用相同的父键值。此模型允许外键为空。...

外键上的NOT NULL约束

如果外键不允许使用空值,则子表中的每一行都必须显式引用父键中的值,因为外键不允许使用空值。

子表中的任何数量的行都可以引用相同的父键值,因此此模型在父键和外键之间建立了一对多关系。但是,子表中的每一行都必须引用一个父键值。不允许外键中没有值(空值)。上一节中的相同示例可用于说明这种关系。但是,在这种情况下,员工必须参考特定部门。

外键的唯一约束

当在外键上定义UNIQUE约束时,子表中只有一行可以引用给定的父键值。此模型允许外键为空。

此模型在父键和外键之间建立了一对一的关系,从而允许外键中的值不确定(空值)。例如,假设员工表中有一个名为MEMBERNO的列,它引用公司保险计划中的员工成员编号。此外,名为INSURANCE的表具有名为MEMBERNO的主键,并且该表的其他列保留与员工保险单有关的相应信息。员工表中的MEMBERNO必须既是外键又是唯一键:

  • 在EMP_TAB和INSURANCE表之间强制执行引用完整性规则(FOREIGN KEY约束)

  • 为了确保每个员工都有唯一的会员编号(UNIQUE键约束)

外键上的UNIQUE和NOT NULL约束

当在外键上同时定义UNIQUE和NOT NULL约束时,子表中只有一行可以引用给定的父键值,并且由于外键中不允许使用NULL值,因此子表中的每一行都必须显式引用父键中的值。

看到这个:

Oracle 11g链接


16

是的,外键可以为空,就像上面的高级程序员所说的那样...我将添加另一种情况,要求外键为空...。假设我们在允许对图片和图片进行注释的应用程序中有表格注释,图片和视频视频。在注释表中,我们可以有两个外键PicturesId和VideosId以及主键CommentId。因此,当您在视频上发表评论时,仅视频ID是必填项,而PictureId将为null ...,如果您在图片上发表评论,则仅需PictureId,而VideosId将为null ...


1
我认为有解决此问题的更好方法。您可以有两列,而不是创建新列,即“ id”和“ type”,其中将包含外键表的id和名称。例如,id = 1,type = Picture将表示到ID为1的Picture表的链接。使用此解决方案的优点是,当将注释添加到其他表时,您将不必创建新列。缺点是在数据库级别没有外键约束,而该约束必须是应用程序级别。
Agent47DarkSoul '16

4
@Agent:我们在生产使用中有了这个“解决方案”。不要这样做,太可怕了。进行查询变得一团糟,“如果是类型1,请加入该表,否则请加入该表”。这对我们来说是一场噩梦。我们最终做了这个答案说的话,并为每种连接类型创建了一个新列。创建列很便宜。几乎唯一的缺点是许多列使Toad难以使用,但这只是Toad的一个缺点。
user128216 '16

1
@FighterJet Rails提供了一个很棒的ORM框架,使用此解决方案甚至可以处理复杂的查询。
Agent47DarkSoul 2016年

2
@Agent:也许可以,但是如果您可以简化它,为什么还要使其复杂?也许“噩梦”是一个错误的用词:非常不便。我们并没有遭受数据完整性问题的困扰。
user128216 '16

7

这取决于这foreign key在您的关系中扮演什么角色。

  1. 如果这foreign key也是key attribute您的关系,则不能为NULL
  2. 如果这foreign key是您关系中的普通属性,则可以为NULL。

3

这是一个使用Oracle语法的示例:
首先让我们创建一个表COUNTRY

CREATE TABLE TBL_COUNTRY ( COUNTRY_ID VARCHAR2 (50) NOT NULL ) ;
ALTER TABLE TBL_COUNTRY ADD CONSTRAINT COUNTRY_PK PRIMARY KEY ( COUNTRY_ID ) ;

创建表省

CREATE TABLE TBL_PROVINCE(
PROVINCE_ID VARCHAR2 (50) NOT NULL ,
COUNTRY_ID  VARCHAR2 (50)
);
ALTER TABLE TBL_PROVINCE ADD CONSTRAINT PROVINCE_PK PRIMARY KEY ( PROVINCE_ID ) ;
ALTER TABLE TBL_PROVINCE ADD CONSTRAINT PROVINCE_COUNTRY_FK FOREIGN KEY ( COUNTRY_ID ) REFERENCES TBL_COUNTRY ( COUNTRY_ID ) ;

这在Oracle中运行得很好。请注意,第二个表中的COUNTRY_ID外键没有“ NOT NULL”。

现在在PROVINCE表中插入一行,仅指定PROVINCE_ID就足够了。但是,如果您还选择指定COUNTRY_ID,则它必须已经存在于COUNTRY表中。


1

默认情况下,外键没有约束,外键可以为null和重复。

在创建表/更改表时,如果添加任何唯一性约束或不为null,则只有它将不允许null /重复值。


0

简而言之,实体之间的“非识别”关系是ER模型的一部分,并且在设计ER图时在Microsoft Visio中可用。这是强制实施“零或大于零”或“零或一”类型的实体之间的基数所必需的。注意基数为“零”,而不是“一对多”中的“一”。

现在,基数可能为“零”(非标识)的非标识关系的示例是当我们说一个实体中的记录/对象时,“可能”或“可能不会”具有一个值作为对记录的引用/ s在另一个实体B中。

由于实体A的一条记录有可能将自己标识为另一实体B的记录,因此实体B中应有一列,以具有实体B的记录的标识值。如果实体-A中没有记录标识实体-B中的记录(或对象),则此列可以为“ Null”。

在面向对象(现实世界)范式中,B类对象的存在并不一定依赖于(强烈耦合)A类对象,这意味着B类与B类松散耦合。这样,A类可以“包含”(包含)A类对象,而不是B类对象的概念必须具有(Composition)A类对象,因为其(Class-object对象) B)创作。

从SQL查询的角度来看,您可以查询实体B中为“实体B”保留的外键“不为空”的所有记录。这将为实体A中的行带来具有特定对应值的所有记录,或者所有具有Null值的记录将成为实体B中的实体A中没有任何记录的记录。


-1

我认为最好考虑表格中可能存在的基数。我们可以将最小基数设为零。如果为可选,则来自相关表的元组的最小参与度可能为零。现在,您必须将外键值允许为null。

但是答案是这一切都取决于业务。


-3

外键的概念基于引用主表中已经存在的值的概念。这就是为什么在另一个表中将其称为外键的原因。这个概念称为参照完整性。如果将外键声明为空字段,则将违反引用完整性的逻辑。它指的是什么?它只能引用主表中存在的内容。因此,我认为将外键字段声明为null是错误的。


它可以引用“什么都没有”,或者您还不知道它的值NULL,但是引用完整性表示的是,如果引用“某事”,它必须在那里。
yaxe

-7

我认为一个表的外键也是其他表的主键,所以它不允许空值,所以在外键中没有空值是没有问题的。

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.