我记得在播客014中听到Joel Spolsky提到他几乎从未使用过外键(如果我没记错的话)。然而,对我来说,它们对于避免数据库中的重复和后续数据完整性问题非常重要。

人们是否有一些可靠的理由(以避免与Stack Overflow原则一致的讨论)?

编辑:“我还没有创建外键的理由,所以这可能是我真正建立一个外键的第一个理由。”


当前回答

它们会使删除记录变得更加麻烦——当其他表中存在外键违反约束的记录时,您就不能删除“主”记录。可以使用触发器进行级联删除。

如果不明智地选择主键,则更改该值将变得更加复杂。例如,如果我有我的“客户”表的PK作为人的名字,并使该键在“订单”表中为FK,如果客户想更改他的名字,那么这是一个巨大的痛苦……但这只是粗制滥造的数据库设计。

我相信使用火密钥的优点大于任何假定的缺点。

其他回答

我还认为外键在大多数数据库中是必要的。唯一的缺点(除了强制一致性带来的性能损失之外)是使用外键允许人们编写假定存在功能外键的代码。这绝不应该被允许。

例如,我看到有人编写了一些代码,将插入插入引用表中,然后尝试将插入插入引用表中,而不验证第一次插入是否成功。如果稍后删除外键,则会导致数据库不一致。

您也不能在更新或删除时假设特定的行为。无论是否存在外键,您仍然需要编写代码来执行您想要的操作。如果您假设删除是级联的,但实际上它们不是级联的,那么您的删除将失败。如果您假定对引用列的更新被传播到引用行,但实际上没有,那么您的更新将失败。出于编写代码的目的,最好不要使用这些特性。

如果打开了这些特性,那么您的代码无论如何都会模仿它们,并且会损失一些性能。

所以,总结....如果需要一致的数据库,外键是必不可少的。在您编写的代码中,永远不应假定外键存在或起作用。

我听到的观点是前端应该有这些业务规则。当您一开始就不应该允许任何破坏约束的插入时,外键会“增加不必要的开销”。我同意吗?不,但我一直都是这么听说的。

编辑:我猜他指的是外键约束,而不是外键这个概念。

使用外键的原因:

you won't get Orphaned Rows you can get nice "on delete cascade" behavior, automatically cleaning up tables knowing about the relationships between tables in the database helps the Optimizer plan your queries for most efficient execution, since it is able to get better estimates on join cardinality. FKs give a pretty big hint on what statistics are most important to collect on the database, which in turn leads to better performance they enable all kinds of auto-generated support -- ORMs can generate themselves, visualization tools will be able to create nice schema layouts for you, etc. someone new to the project will get into the flow of things faster since otherwise implicit relationships are explicitly documented

不使用外键的原因:

you are making the DB work extra on every CRUD operation because it has to check FK consistency. This can be a big cost if you have a lot of churn by enforcing relationships, FKs specify an order in which you have to add/delete things, which can lead to refusal by the DB to do what you want. (Granted, in such cases, what you are trying to do is create an Orphaned Row, and that's not usually a good thing). This is especially painful when you are doing large batch updates, and you load up one table before another, with the second table creating consistent state (but should you be doing that sort of thing if there is a possibility that the second load fails and your database is now inconsistent?). sometimes you know beforehand your data is going to be dirty, you accept that, and you want the DB to accept it you are just being lazy :-)

我认为(我不确定!)大多数已建立的数据库都提供了一种指定外键的方法,这种方法不是强制的,只是一些元数据。由于不强制执行消除了不使用fk的所有理由,如果第二部分中的任何理由适用,您可能应该走那条路。

我同意德米特里的话,但要补充一点。

我在一个批处理计费系统中工作,需要在30多个表中插入大量的行。我们不允许做数据泵(Oracle),所以我们必须做批量插入。这些表上有外键,但我们已经确保它们不会破坏任何关系。

在插入之前,我们禁用外键约束,这样Oracle就不会一直进行插入。插入成功后,我们重新启用约束。

PS:在一个大型数据库中,一条记录有许多外键和子行数据,有时外键可能不好,您可能希望禁止级联删除。对于在计费系统中的我们来说,如果进行级联删除,将花费太长时间,并且对数据库造成太大负担,因此我们只是在主驱动程序(父)表上使用一个字段将记录标记为坏记录。

我也听过这种说法——有些人忘记在外键上放索引,然后抱怨某些操作很慢(因为约束检查可以利用任何索引)。所以总结一下:没有好的理由不使用外键。所有现代数据库都支持级联删除,所以…