请为我澄清两件事:
- 外键可以为空吗?
- 外键可以重复吗?
据我所知,外键不应该使用 NULL
,但在我的一些应用程序中,我可以在 Oracle 和 SQL Server 中输入 NULL
,而且我不知道为什么。
请为我澄清两件事:
据我所知,外键不应该使用 NULL
,但在我的一些应用程序中,我可以在 Oracle 和 SQL Server 中输入 NULL
,而且我不知道为什么。
简单回答:是的,它可以为空或重复。
我想解释一下为什么一个外键可能需要为空或需要唯一或不唯一。首先记住,一个外键只需要该字段中的值必须首先存在于另一个表(父表)中,这就是定义中的FK。按照定义,空值并不是一个值。空值意味着我们尚不知道该值是什么。
让我举个现实生活的例子。假设您有一个存储销售提案的数据库。更进一步地说,假设每个提案只分配了一个销售人员和一个客户。因此,您的提案表将具有两个外键,一个与客户ID相关联,另一个与销售代表ID相关联。但是,在创建记录时,并不总是会分配销售代表(因为没有人有时间去处理它),因此填写客户ID,而销售代表ID可能为空。换句话说,通常情况下,当您在输入数据时可能不知道其值,但是您确实知道其他需要输入的表格中的值时,需要能够具有一个空的FK。要允许FK中的空值,通常只需允许在具有FK的字段上允许空值即可。空值与FK的概念是分开的。
它是否唯一或不唯一与该表是否具有一对一或一对多与父表的关系有关。如果您有一个一对一的关系,那么将所有数据都存储在一个表格中是可能的,但是如果表格变得太宽或者数据属于不同的主题(例如@tbone给出的“员工-保险”示例),那么就需要使用带有FK的单独的表格。然后,您要么希望将此FK也作为PK(这可以保证唯一性),要么在其上放置唯一约束。
大多数FK是用于一对多关系的,这就是从FK中获取而无需在该字段上添加进一步约束的内容。例如,您有一个订单表和一个订单详细信息表。如果客户一次订购10件物品,则他有一个订单和十个包含相同订单ID(FK)的订单详细记录。
2 - 是的,只要它不是唯一约束或与唯一索引相关联。
是的,外键可以为空,如上述高级程序员所述...我想再增加一种场景,需要外键为空... 假设我们在一个允许在图片和视频上进行评论的应用中有评论、图片和视频三个表。在评论表中,我们可以有两个外键PicturesId和VideosId,以及主键CommentId。因此,当你在视频上发表评论时,只需要VideosId,而pictureId将为空...如果你在图片上发表评论,则只需要PictureId,而VideosId将为空...
这取决于该“外键”在您关系中扮演的角色。
这里是一个使用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表中。
默认情况下,外键没有任何约束条件,外键可以为null和重复。
在创建表格/更改表格时,如果添加任何唯一性或非空约束,则只有它不允许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);
我认为最好考虑表中可能存在的基数。我们可能会有最小基数为零的情况。当它是可选的时候,相关表中元组的最小参与度可以为零,现在你面临的问题是允许外键值为空的必要性。
但答案是一切都取决于业务。
NULL
,但是引用完整性要求如果它引用了“something”,那么这个“something”必须存在。 - yaxe