ChatGPT解决这个技术问题 Extra ChatGPT

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

请为我澄清两件事:

外键可以为NULL吗?外键可以重复吗?

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

@Adrian:据我所知,外键不能为空,但它在 sql server 和 oracle 中为空。你能解释一下为什么吗?
@Jams - 阅读我的答案中的链接。
这不能被删除,因为答案和问题很有用。随意编辑问题以改进它。
请把关于重复的问题分开。下面只回答关于 NULL 的问题。

A
Anonymous Y - 杨z强

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

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

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

是否唯一与表与父表是一对一还是一对多的关系有关。现在,如果您有一对一的关系,您可以将数据全部放在一个表中,但是如果表变得太宽或者数据是关于不同的主题(员工 - 保险示例 @tbone 给出例如),那么您需要带有 FK 的单独表。然后,您可能希望将此 FK 也设为 PK(保证唯一性)或对其施加唯一约束。

大多数 FK 是针对一对多关系的,这就是您从 FK 获得的,而无需在字段上添加进一步的约束。例如,您有一个订单表和订单详细信息表。如果客户一次订购十件商品,则他有一份订单和十份订单明细记录,其中包含与 FK 相同的 orderID。


所以这比有一个名为“Unassigned”的假销售人员更好吗?
一条评论。 Null 为不知道 SQL(错误)如何处理 3VL 的人们在查询中留下了很大的错误空间。如果某个 r-table 确实不需要销售人员,只是您不包括该记录。一个单独的表可以是“ProposalAssignedTo”或类似的,具有适当的约束。然后,查询编写器可以连接到该表,并在提案没有销售人员时为我们想做的任何事情提供自己的逻辑。 NULL 不仅仅意味着“我们不知道”——它可以用于很多事情(这就是为什么它几乎总是一个坏主意)
@ThomasWeller 引用假销售员(“未分配”)会使问题变得更糟。我假设您的销售人员表有多个列......? Unassigned 先生的社会保险号码是多少?他被分配到哪个部门?他的老板是谁?我希望你明白我的意思:当你创建一个“未分配”的销售人员时,你很快就会发现你用一个表中的 NULL 换取了另一个表中的多个 NULL
@ThomasWeller 如果/当您需要本地化界面时,您也会遇到问题。
惊人的例子,要走的路。
C
ChrisW

从马口中:

即使没有匹配的 PRIMARY 或 UNIQUE 键,外键也允许键值全部为 NULL核心价值。此模型允许在外键中使用空值。 ... 外键上的 NOT NULL 约束 当外键中不允许空值时,子表中的每一行都必须显式引用父键中的值,因为外键中不允许空值。子表中任意数量的行都可以引用相同的父键值,因此该模型在父键和外键之间建立了一对多的关系。但是,子表中的每一行都必须引用一个父键值;不允许外键中没有值(null)。上一节中的相同示例可用于说明这种关系。但是,在这种情况下,员工必须具有对特定部门的引用。外键上的 UNIQUE 约束 当外键上定义了 UNIQUE 约束时,子表中只有一行可以引用给定的父键值。此模型允许在外键中使用空值。此模型在父键和外键之间建立一对一的关系,允许外键中存在未确定的值(空值)。例如,假设员工表有一个名为 MEMBERNO 的列,指的是公司保险计划中的员工会员编号。此外,名为 INSURANCE 的表有一个名为 MEMBERNO 的主键,该表的其他列保存与员工保险单相关的相应信息。员工表中的 MEMBERNO 必须既是外键又是唯一键: 强制执行 EMP_TAB 和 INSURANCE 表之间的参照完整性规则(FOREIGN KEY 约束) 保证每个员工都有唯一的成员编号(UNIQUE 键约束)外键上的 UNIQUE 和 NOT NULL 约束当在外键上同时定义 UNIQUE 和 NOT NULL 约束时,子表中只有一行可以引用给定的父键值,并且由于外键中不允许 NULL 值,子表中的每一行都必须显式引用父键中的值。

看到这个:

Oracle 11g link


J
JNK

1 - Yes, since at least SQL Server 2000.

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


该链接已失效。
甚至 WaybackMachine 也没有任何记录。最早的快照可以追溯到 2016 年,但即便如此,它仍显示该文档已退休。
T
Touseef Ahmed Awan

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


我认为有更好的方法来解决这个问题。您可以有两列,即“id”和“type”,而不是创建新列,它们将包含外键表的 id 和名称。例如,id=1,type=Picture 将表示指向 id 为 1 的 Picture 表的链接。使用此解决方案的优点是,在将注释添加到其他表时,您不必创建新列。缺点是数据库级别没有外键约束,而约束必须是应用程序级别。
@Agent:我们在生产使用中已经有了这个“解决方案”。不要这样做,太可怕了。进行查询变得如此混乱,“如果它是类型 1,则加入此表,否则加入此表”。这对我们来说是一场噩梦。我们最终按照这个答案所说的去做,并为每种类型的连接创建了一个新列。创建列很便宜。几乎唯一的缺陷是许多列使 Toad 难以使用,但这只是 Toad 的一个缺陷。
@FighterJet Rails 提供了一个很棒的 ORM 框架,可以用这个解决方案处理复杂的查询。
@Agent:也许它可以......但是如果你可以让它变得简单,为什么让它变得复杂?也许“噩梦”这个词用错了:它很不方便。我们没有遭受数据完整性问题(很多)。
s
shinxg

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

如果这个外键也是你关系中的一个键属性,那么它不能为 NULL 如果这个外键是你关系中的一个普通属性,那么它可以是 NULL。


key attribute 是什么意思?
M
Mouhcine

下面是一个使用 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 表中。


n
nitin lalwani

默认情况下,外键没有限制,外键可以为空和重复。

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


F
Fakhar

简而言之,实体之间的“非识别”关系是 ER-Model 的一部分,在设计 ER-Diagram 时可在 Microsoft Visio 中使用。这是在“零或大于零”或“零或一”类型的实体之间强制执行基数所必需的。请注意基数中的“零”而不是“一对多”中的“一”。

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

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

在面向对象(现实世界)范式中,存在 B 类对象的存在不一定依赖于(强耦合)A 类对象的情况,这意味着 B 类与与 B 类对象的概念相反,A 类可以“包含”(包含)A 类对象的一个对象必须具有(组合)A 类对象,因为它的(类对象- B) 创造。

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


L
Lukasz Szozda

外键可以为NULL吗?

现有答案侧重于单列方案。如果我们考虑多列外键,我们有更多使用 SQL 标准中定义的 MATCH [SIMPLE | PARTIAL | FULL] 子句的选项:

PostgreSQL-CREATE TABLE 插入引用列的值与使用给定匹配类型的被引用表和被引用列的值匹配。共有三种匹配类型:MATCH FULL、MATCH PARTIAL 和 MATCH SIMPLE(这是默认设置)。 MATCH FULL 将不允许多列外键的一列为空,除非所有外键列都为空;如果它们都为空,则该行不需要在引用的表中具有匹配项。 MATCH SIMPLE 允许任何外键列为空;如果其中任何一个为空,则该行不需要在引用的表中具有匹配项。 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


R
Rick Gittins

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

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


S
SQLDev

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


它可以引用“无”,或者您还不知道它的值 NULL ,但参照完整性的意思是,如果它引用“某物”,它必须在那里。
u
user4532553

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