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

时间:2011-09-27 17:57:45

标签: sql sql-server oracle foreign-keys

请为我澄清两件事:

  1. 外键可以为NULL吗?
  2. 外键可以重复吗?
  3. 据我所知,NULL不应该用在外键中,但在我的某些应用程序中,我可以在Oracle和SQL Server中输入NULL,而且我不会不知道为什么。

12 个答案:

答案 0 :(得分:429)

简答:是的,它可以是NULL或重复。

我想解释为什么外键可能需要为null或者可能需要唯一或不唯一。首先要记住,外键只需要该字段中的值必须首先存在于另一个表(父表)中。根据定义,这就是FK。根据定义,空值不是值。 Null意味着我们还不知道它的价值是什么。

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

它是唯一的还是不唯一的,与表是否与父表具有一对一或一对多的关系有关。现在,如果你有一对一的关系,你有可能将数据全部放在一个表中,但如果表格太宽或者数据是在另一个主题上(员工 - 保险示例@tbone给出了)例如),那么你想要一个带有FK的单独表。然后你想要使这个FK也是PK(它保证唯一性)或对它施加一个独特的约束。

大多数FK用于一对多的关系,这是你从FK获得的,而不在场上添加进一步的约束。所以你有一个订单表和订单详情表。如果客户一次订购十件商品,他有一个订单和十个订单明细记录,其中包含与FK相同的订单ID。

答案 1 :(得分:46)

1 - Yes, since at least SQL Server 2000.

2 - 是的,只要它不是UNIQUE约束或链接到唯一索引。

答案 2 :(得分:38)

从马的嘴里说:

  

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

     

外键没有限制

     

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

     

外键上的NOT NULL约束

     

当不允许空值时   一个外键,子表中的每一行必须显式引用一个   父键中的值,因为外部不允许空值   键。

     

子表中的任意数量的行都可以引用同一个父级   键值,因此该模型建立了一对多的关系   在父键和外键之间。但是,孩子的每一行   table必须具有父键值的引用;缺席了   不允许外键中的值(null)。同样的例子   上一节可以用来说明这种关系。   但是,在这种情况下,员工必须具有特定的参考   部。

     

外键上的UNIQUE约束

     

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

     

此模型在父级之间建立一对一的关系   和允许未确定值(空值)的外键   外键。例如,假设employee表有一列   名为MEMBERNO,指的是员工会员编号   公司保险计划。此外,名为INSURANCE的表具有主表   键名为MEMBERNO,表中的其他列保持各自   有关员工保险单的信息。 MEMBERNO in   employee表必须是外键和唯一键:

     
      
  • 在EMP_TAB和EMP_TAB之间强制实施参照完整性规则   保险表(FOREIGN KEY约束)

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

  •   
     

外键上的UNIQUE和NOT NULL约束

     

两者都是UNIQUE   和NOT NULL约束是在外键上定义的,只有一行   在子表中可以引用给定的父键值,因为   子项中的每一行中都不允许使用NULL值   table必须显式引用父键中的值。

见:

Oracle 11g link

答案 3 :(得分:15)

是的,外键可以为null,如上面高级程序员所说......我会添加另一个场景,其中外键需要为null .... 假设我们在应用程序中有表格注释,图片和视频,允许对图片和视频进行评论。在注释表中,我们可以有两个外键PicturesId,以及VideosId和主键CommentId。因此,当您对视频发表评论时,仅需要VideosId并且pictureId将为null ...如果您对图片发表评论,则只需要PictureId,而且VideosId将为null ...

答案 4 :(得分:5)

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

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

答案 5 :(得分: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 ) ;

创建表PROVINCE

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表中。

答案 6 :(得分:1)

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

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

答案 7 :(得分:0)

简单地说,实体之间的“非识别”关系是ER-Model的一部分,在设计ER-Diagram时可以在Microsoft Visio中使用。这需要在“零或大于零”或“零或一”类型的实体之间强制实现基数。注意基数中的这个“零”而不是“一对多”中的“一”。

现在,基数可能为“零”(非识别)的非识别关系的例子是当我们说一个实体中的记录/对象时 - “可能”或“可能不”具有作为参考的值到另一个实体-B的记录。

因为,实体-A的一条记录有可能将自己标识为其他实体-B的记录,因此实体-B中应该有一列具有实体记录的标识值 - B.如果实体-A中的记录没有标识实体-B中的记录/或(对象/ s),则该列可以是“空”。

在面向对象(真实世界)范例中,有些情况下,类B的对象不一定依赖于(强耦合)在类A的对象上它的存在,这意味着类B是松散耦合的使用A类使得A类可以“包含”(包含)A类对象,而B类对象的概念必须具有(组合)A类对象,因为它(对象) B类创造。

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

答案 8 :(得分:0)

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

答案 9 :(得分:0)

外键可以为NULL吗?

现有答案集中在单列情况下。如果考虑使用多列外键,则可以使用SQL标准中定义的MATCH [SIMPLE | PARTIAL | FULL]子句来选择更多选项:

PostgreSQL-CREATE TABLE

使用给定的匹配类型,将插入到引用列中的值与引用表和引用列的值进行匹配。共有三种匹配类型:“完全匹配”,“部分匹配”和“简单匹配”(默认)。 MATCH FULL (全部匹配)不允许多列外键的一列为空,除非所有外键列均为空;如果它们全为空,则不需要该行在引用表中具有匹配项。 MATCH SIMPLE (MATCH SIMPLE )允许任何外键列为空;如果它们中的任何一个为null,则不需要该行在引用表中具有匹配项。 MATCH PARTIAL 尚未实现。

(当然,可以将NOT NULL约束应用于引用列,以防止出现这些情况。)

示例:

CREATE TABLE A(a VARCHAR(10), b VARCHAR(10), d DATE , UNIQUE(a,b));
INSERT INTO A(a, b, d) 
VALUES (NULL, NULL, NOW()),('a', NULL, NOW()),(NULL, 'b', NOW()),('c', 'b', NOW());

CREATE TABLE B(id INT PRIMARY KEY, ref_a VARCHAR(10), ref_b VARCHAR(10));

-- MATCH SIMPLE - default behaviour nulls are allowed
ALTER TABLE B ADD CONSTRAINT B_Fk FOREIGN KEY (ref_a, ref_b) 
REFERENCES A(a,b) MATCH SIMPLE;

INSERT INTO B(id, ref_a, ref_b) VALUES (1, NULL, 'b');  

-- (NULL/'x') 'x' value does not exists in A table, but insert is valid
INSERT INTO B(id, ref_a, ref_b) VALUES (2, NULL, 'x');  

ALTER TABLE B DROP CONSTRAINT IF EXISTS B_Fk; -- cleanup

-- MATCH PARTIAL - not implemented
ALTER TABLE B ADD CONSTRAINT B_Fk FOREIGN KEY (ref_a, ref_b) 
REFERENCES A(a,b) MATCH PARTIAL;
-- ERROR:  MATCH PARTIAL not yet implemented

DELETE FROM B; ALTER TABLE B DROP CONSTRAINT IF EXISTS B_Fk; -- cleanup

-- MATCH FULL nulls are not allowed
ALTER TABLE B ADD CONSTRAINT B_Fk FOREIGN KEY (ref_a, ref_b) 
REFERENCES A(a,b) MATCH FULL;

-- FK is defined, inserting NULL as part of FK
INSERT INTO B(id, ref_a, ref_b) VALUES (1, NULL, 'b');
-- ERROR:  MATCH FULL does not allow mixing of null and nonnull key values.

-- FK is defined, inserting all NULLs - valid
INSERT INTO B(id, ref_a, ref_b) VALUES (1, NULL, NULL);

db<>fiddle demo

答案 10 :(得分:-1)

我认为最好考虑表格中可能的基数。 我们可以有最小基数为零。当它是可选的时,相关表中元组的最小参与可能为零,现在你面临允许外键值为null的必要性。

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

答案 11 :(得分:-7)

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