引用非主键列的外键约束有哪些缺点?

3
我想知道在引用关系中使用主键列与唯一键列之间是否存在任何缺点(在SQL Server中,外键约束只能引用主键或唯一索引中的列)。
在特定的DB系统(例如Microsoft SQL Server 2005)中,如果外键引用主键与唯一键之间存在差异,查询解析方式是否有所不同?
请注意,我并不是在询问使用不同数据类型的列进行引用完整性、连接等操作时的差异。
纯粹作为一个例子,想象一个数据库,在其中有一个“查找表”dbo.Offices:
CREATE TABLE dbo.Offices (
    ID   int NOT NULL IDENTITY(1,1) CONSTRAINT PK_Codes PRIMARY KEY,
    Code varchar(50) NOT NULL CONSTRAINT UQ_Codes_Code UNIQUE
);

还有一张表格dbo.Patients:

CREATE TABLE dbo.Patients (
    ID         int NOT NULL IDENTITY(1,1) CONSTRAINT PK_Patients PRIMARY KEY,
    OfficeCode varchar(50) NOT NULL,
    ...
    CONSTRAINT FK_Patients_Offices FOREIGN KEY ( OfficeCode )
        REFERENCES dbo.Offices ( Code )
);

与上面T-SQL代码中的dbo.Patients表及其约束FK_Patients_Offices相比,以下备选版本存在哪些缺点:

CREATE TABLE dbo.Patients (
    ID       int NOT NULL IDENTITY(1,1) CONSTRAINT PK_Patients PRIMARY KEY,
    OfficeID int NOT NULL,
    ...
    CONSTRAINT FK_Patients_Offices FOREIGN KEY ( OfficeID )
        REFERENCES dbo.Offices ( ID )
);

显然,对于dbo.Patients的第二个版本,如果在dbo.OfficesCode列中的值发生更改,则不需要更新OfficeID列中的值。

另外(显然),使用dbo.OfficesCode列作为外键引用在很大程度上会削弱代理键列ID的目的 - 这纯粹是示例的产物。[是否有更好的表格示例,其中外键引用可能合理地使用非主键?]

我开始认为主键的唯一性可能意味着引用主键的外键与引用唯一键的外键之间的差异(例如性能)是微不足道的。 - Kenny Evitt
1
如果办公室代码可能会更改,建议在外键约束上使用ON UPDATE CASCADE - Damien_The_Unbeliever
@Damine_The_Unbeliever - 那是很好的建议,但与我的问题无关。 - Kenny Evitt
6个回答

4
为什么你认为会有任何缺点呢?
恰恰相反!很高兴看到你正在执行引用完整性,每个人都应该这样做!没有缺点 - 只是这样做的好习惯!
我没有看到引用唯一索引与引用主键之间有任何功能上的区别或问题/问题。
更新:由于您不关心性能或数据类型相关问题,因此此最后一段可能不会增加任何附加值。
我唯一看到的小问题是您的OfficeCode既是VARCHAR,因此您可能会遇到排序和/或大小写(根据您的排序)问题,并且基于相当大(长达50字节)和可变长度字段的JOIN条件可能不如基于小型,固定长度INT列的JOIN条件有效。

1
我们的数据库不区分大小写,而我们的应用程序区分大小写,因此我可以证实您通过使用VARCHARs提出的问题。就在上周遇到了这样的问题。 - Calvin Allen
我在我的问题中添加了一些内容,希望能够澄清我的问题。 我并不是在询问引用完整性本身的缺点(其中至少有性能成本[和收益])。 我也没有询问使用“int”列与使用“varchar”列作为关键列之间的区别。 - Kenny Evitt
@Zerofiz:在大多数企业中,数据库先于应用程序存在。一个应用程序如果对其数据库的工作方式做出不必要的假设,则存在应用程序设计问题,而非数据库设计问题。 - Mike Sherrill 'Cat Recall'

4

没有缺点。

然而...

为什么你在Offices表中有一个ID列?当作为其他表的外键时,使用代理键可以减少空间并提高性能,相对于使用varchar列。

如果您将varchar列用作外键,则不需要代理键。

使用Code列作为FKs浪费了IDENTITY的大部分好处。


1
“使用IDENTITY时,大多数好处都被在FKs中使用Code列所浪费。但是,拥有IDENTITY的主要缺点,即需要另一个连接才能获得可读输出,可以通过使用另一个候选键来巧妙地避免。在这里保存一次连接,那里保存一次连接,很快以前需要九个连接的查询现在只需要两个。这可能会带来很大的性能提升。(但可能不适用于OP的情况。)” - Mike Sherrill 'Cat Recall'
2
@catcall,尽管如此,当PK更改时需要更新FK键字段并持续重新排列聚簇索引记录以及使用varchar而不是int进行连接而导致的性能损失抵消了该增益。 - HLGEM
1
@HLGM:你可能会感到惊讶。我们使用自然键测试了我们的内部系统。主键不经常更改。80%的查询比使用ID号码更快,主要是因为连接较少。在某些情况下,使用ID号码和大量连接的查询需要30倍的时间才能返回结果。 - Mike Sherrill 'Cat Recall'
我现在可以看出,通过添加一个“对称”的例子来澄清我的问题可能会更好,这样我就不会询问使用由varchar列组成的键与使用由int列组成的键之间相对优劣的问题,也不会关心有关使用代理键或自然键的宗教战争。我只是纯粹地关心使用主键和唯一键作为外键约束的参考之间的区别。 - Kenny Evitt
如果Code列用于外键引用,那么dbo.Offices表包括ID列的冗余性是完全正确的。 - Kenny Evitt
显示剩余2条评论

1

主键是候选键,与任何其他候选键没有根本区别。广泛的惯例是,在每个表中指定一个候选键作为“主”键,并将其用于所有外键引用。

这种方式单独选择一个键的可能优点是,使数据库用户更清楚地使用该键:他们知道被引用的键是哪个,而不必查看每个引用表。然而,这完全是可选的。如果您发现以其他方式更方便,或者要求规定某些其他键应由外键引用,则建议您这样做。


我重新阅读了你的回答,你确实指出了使用非主键作为外键引用的一个重要缺点——它是意外的。对于我所使用的其中一个应用程序来说,这是一个主要的烦恼——许多表都是通过它们的“代码”列(几乎肯定是为了在编写即席查询时更好地读取数据)而不是“自动-ID”主键列进行引用的。 - Kenny Evitt

0
我能想到最重要的一点是,如果他们重新编号办公室,你将失去完整性或需要更新两个表。不过,这种情况可能性有多大呢。
除非您拥有非常大的办公室代码,否则性能影响微乎其微,即使有,也比您预期的要小得多。
对于大多数人来说,这不被认为是数据库设计的重要决定因素。

1
如果一个办公室的ID发生变化,那么对于OfficeCode的引用仍然是完全有效的......或者你的意思是什么?当有人更改OfficeCode时?可以在FK关系中添加ON UPDATE CASCADE子句来管理它... - marc_s
如果您的办公室号码(“w-823”,ID=562)由于拆分为两个(W-823a和W-823b),例如从“W-B23”更改为“W-B23a”,则办公桌可以更改而无需更改您的患者记录。 - dkretz
1
好的,说得对 - 而且正如我所提到的,像 INT 这样较小的 ID 上进行 JOINs 也很可能更有利于性能。 - marc_s
1
@Kenny Evitt,但是作为代理键的PK比基于字符串的字段更不可能发生变化。这就是为什么如果你有代理键来强制执行引用完整性,通常最好使用代理键的原因。 - HLGEM
@HLGEM - 你说得对;这是我在处理过的应用程序中遇到的性能问题,因此我问了关于数据类型是否不是“大多数人数据库设计的重要决定因素”的问题。如果这对大多数人来说不是一个重要的设计决策,那么它应该是。 - Kenny Evitt
显示剩余3条评论

0
假设您在code列上添加了索引(一旦引用它,您肯定应该这样做),那么是否有什么反对完全摆脱ID列并将code列也用作PK的说法?

基于我刚才提到的原因(大键、长度变化的键),仅仅使用 OfficeCode 作为您的主要(因此默认情况下也是聚集键)是一个非常糟糕的想法!不要这样做!! - marc_s
1
一个好的聚集键应该是NUSE - narrow(窄的),unique(唯一的),static(静态的)和ever-increasing(递增的)。请参阅Kim Tripp的优秀博客文章 - marc_s
好的,感谢您提供的见解。我认为"代码即外键"方法的一个主要缺点是它可能会影响可维护性,因为编写视图、联接等操作的新开发人员可能期望ID列是外键。无论您做什么,您在数据库设计方面都应该保持一致。 - bonifaz
在 SQL Server 中(我想象中的任何 SQL DB),由于该列的唯一键约束条件,表 dbo.Offices 的 Code 列会自动创建一个索引。 我在问题中添加了内容以澄清我不是在问有关表 dbo.Offices 的设计。我想知道参照主键与参照唯一键之间的任何差异。 您对于保持一致性的看法是正确的(在便宜实现的情况下,例如新应用程序)。 - Kenny Evitt

-1

严重漏洞 我们能够将一些值输入到dbo.Patients.OfficeID中,而这些值在dbo.Offices.ID中并不存在。 这意味着说没有任何参考意义。


你是如何输入这个值的? - Kenny Evitt
你提出了一个没有证据支持但可能有趣的说法,但无论你的说法是否属实,你后面的陈述都是错误的。 - Kenny Evitt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接