提问



我记得在播客014中听到Joel Spolsky提到他几乎没有使用过外键(如果我没记错的话)。但是,对我来说,在整个数据库中避免重复和后续数据完整性问题似乎非常重要。[18]]] [19]


人们有一些坚实的理由为什么(为了避免与Stack Overflow原则一致的讨论)?


 我还没有理由创建外键,所以这可能是我实际设置外键的第一个理由。 [20]

最佳参考


使用外键的原因:



  • 你不会得到孤儿行

  • 你可以获得删除级联行为,自动清理表格

  • 了解数据库中表之间的关系有助于Optimizer计划您的查询以实现最高效的执行,因为它能够更好地估计连接基数。

  • FK对数据库中最重要的统计数据给出了相当大的提示,从而提高了性能

  • 它们支持各种自动生成的支持 - ORM可以自行生成,可视化工具将能够为您创建漂亮的模式布局等。

  • 项目的新手将更快地进入事物流,因为明确记录了隐含的关系



不使用外键的原因:



  • 您正在使每个CRUD操作的DB工作更多,因为它必须检查FK一致性。如果你有很多流失,这可能是一个很大的代价

  • 通过强制执行关系,FK指定您必须添加/删除内容的顺序,这可能导致DB拒绝执行您想要的操作。 (当然,在这种情况下,你要做的是创建一个孤立行,而这通常不是一件好事。)当你进行大批量更新时,这会特别痛苦,并且你在另一个表之前加载一个表,第二个表创建一致状态(但如果第二个加载失败并且您的数据库现在不一致,那么您是否应该做这类事情?)。

  • 有时你事先知道你的数据会变脏,你接受了,你希望数据库接受它

  • 你只是在偷懒: - )



我认为(我不确定!)大多数已建立的数据库提供了一种指定未强制执行的外键的方法,并且只是一些元数据。由于非执法部门消除了不使用FK的所有理由,如果第二部分中的任何原因适用,您应该走这条路线。[21]

其它参考1


这是一个培养问题。如果你在教育或职业生涯的某个地方花时间喂养和照顾数据库(或与那些有才能的人一起密切合作),那么实体和关系的基本原则在你的思维过程中根深蒂固。其中的基础是如何/何时/为什么在数据库中指定密钥(主要,外部和备用)。这是第二天性。


但是,如果您在过去与RDBMS相关的工作中没有那么彻底或积极的经验,那么您很可能没有接触到这些信息。或者也许你的过去包括沉浸在一个大声反数据库的环境中(例如,那些DBA是白痴 - 我们很少,我们选择了几个java/c#代码甩卖者将节省一天),在这种情况下你可能会强烈反对一些dweeb的神秘唠叨告诉你,如果你只是倾听,FK(以及他们可以暗示的限制)真的很重要。


大多数人都是在他们还是小孩的时候被教过,刷牙很重要。没有它,你可以过关吗?当然,但是如果你在每顿饭后刷过,那么在某个地方,你的牙齿可用量会比你可以用的少。如果妈妈和爸爸有足够的责任来涵盖数据库设计和口腔卫生,我们就不会有这个会话。 :-)

其它参考2


引用Joe Celko:



  就像26号皮带一样,只因为你可以
  并不意味着你应该!



我确信有很多应用可以让你逃脱它,但这不是最好的主意。您不能始终指望您的应用程序正确管理您的数据库,坦率地说,管理数据库不应该是您的应用程序非常关注的问题。


如果您使用关系数据库,那么您似乎应该在其中定义一些关系。不幸的是,这种态度(你不需要外键)似乎被许多应用程序开发人员所接受,他们宁愿不被数据完整性等愚蠢的事情所困扰(但需要因为他们的公司没有专门的数据库开发人员)。通常在这些类型的数据库中,你很幸运只有主键;)

其它参考3


外键对于任何关系数据库模型都是必需

其它参考4


我总是使用它们,但后来我为金融系统制作数据库。数据库是应用程序的关键部分。如果财务数据库中的数据不完全准确,那么您在代码/前端设计中投入的精力并不重要。你只是在浪费时间。


还有一个事实是,多个系统通常需要直接与数据库接口 - 从刚刚读取数据的其他系统(Crystal Reports)到插入数据的系统(不一定使用我设计的API;它可能是写的由一位愚蠢的经理刚刚发现了VBScript并且拥有SQL框的SA密码。如果数据库不像它可能那样具有白痴性,请再见,再见数据库。


如果您的数据很重要,那么请使用外键,创建一组存储过程来与数据交互,并创建最难的数据库。如果您的数据不重要,为什么要开始使用数据库?

其它参考5


外键使自动化测试复杂化



假设您正在使用外键。您正在编写一个自动测试,上面写着当我更新金融帐户时,它应该保存交易记录。在这个测试中,你只关注两个表:accountstransactions


但是,accountscontracts的外键,contracts有fk到clientsclients有fk到citiescities的fk为states


现在数据库不允许您在没有在与测试无关的四个表中设置数据的情况下运行测试


至少有两种可能的观点:



  • 这是件好事:你的测试应该是现实的,那些数据限制将存在于生产中。

  • 那是件坏事:你应该能够在不涉及其他部分的情况下对系统进行单元测试。您可以为整个系统添加集成测试。



也可以在运行测试时暂时关闭外键检查。 MySQL至少支持这一点。[22]





更新:我现在总是使用外键。我对他们复杂测试的反对意见的答案是编写单元测试,这样他们根本不需要数据库。任何使用数据库的测试都应该正确使用它,包括外键。如果设置很痛苦,找一个不那么痛苦的方法进行设置。

其它参考6


@imphasing - 这正是那种导致维护噩梦的心态。


为什么哦,为什么你会忽略声明性参照完整性,其中数据可以保证至少是一致的,有利于所谓的软件执行,这是一种最好的弱预防措施。

其它参考7


他们可以删除记录更加繁琐 - 你不能删除其他表中有外键会违反该约束的记录的主记录。


重要的是要记住,SQL标准定义了删除或更新外键时所采取的操作。
我所知道的是:



  • ON DELETE RESTRICT - 防止删除另一个表中包含此列中的键的任何行。这就是肯·雷所描述的。

  • ON DELETE CASCADE - 如果删除了另一个表中的一行,请删除此表中引用它的所有行。

  • ON DELETE SET DEFAULT - 如果删除了另一个表中的一行,请将引用它的任何外键设置为默认列。

  • ON DELETE SET NULL - 如果删除了另一个表中的一行,请将此表中引用它的任何外键设置为null。

  • ON DELETE NO ACTION - 这个外键只标记它是外键;即用于OR映射器。



这些相同的行动也适用于ON UPDATE


默认值似乎取决于您正在使用的SQL服务器。/questions/tagged/sql

其它参考8


有一个很好的理由不使用它们:如果你不了解他们的角色或如何使用它们。


在错误的情况下,外键约束可能导致事故的瀑布复制。如果有人删除了错误的记录,撤消它可能会成为一项巨大的任务。


另外,相反,当你需要移除某些东西时,如果设计不当,约束会导致各种阻止你的锁。

其它参考9


没有良好原因不来使用它们......除非孤立的行对我来说不是什么大问题。

其它参考10


更大的问题是:你会带着眼罩开车吗?如果您开发一个没有参考约束的系统,那就是这样。请记住,业务需求变化,应用程序设计更改,代码更改中的各自逻辑假设,逻辑本身可以重构,等等。通常,数据库中的约束是在当代逻辑假设下实现的,对于特定的逻辑断言和假设集似乎是正确的。


在应用程序的生命周期中,参考和数据检查通过应用程序限制警察数据收集,特别是当新要求推动逻辑应用程序更改时。


对于此列表的主题 - 外键本身并不提高性能,也不会从实时事务处理系统的角度显着降低性能。但是,在高容量批处理系统中存在约束检查的聚合成本。所以,这是差异,实时与批量交易过程;批处理 - 通过约束检查产生的经过处理的批次处理成本受到影响。


在设计良好的系统中,数据一致性检查将在处理批次之前完成(尽管如此,此处也存在相关成本);因此,在加载时间内不需要外键约束检查。实际上,在处理批处理之前,应暂时禁用所有约束(包括外键)。


QUERY PERFORMANCE - 如果表连接在外键上,请注意外键列不是INDEXED(尽管相应的主键按定义编制索引)。通过索引外键,就此而言,通过索引任何键,并在索引上连接表有助于提高性能,而不是通过加入具有外键约束的非索引键。


更改主题,如果数据库只是支持网站显示/呈现内容/等并记录点击次数,那么对所有表格具有完全约束的数据库就会被用于此类目的。想一想。大多数网站甚至不使用数据库。对于类似的要求,只是记录数据而不是按照说法引用数据,请使用没有约束的内存数据库。这并不意味着没有数据模型,是逻辑模型,而是没有物理数据模型。

其它参考11


使用外键的其他原因:
- 允许更多地重用数据库


不使用外键的其他原因:
- 您正试图通过减少重用来锁定客户的工具。

其它参考12


在添加记录之前,检查另一个表中是否存在相应的记录是业务逻辑。


以下是您不希望在数据库中出现这种情况的一些原因:



  1. 如果业务规则发生变化,则必须更改数据库。数据库需要在很多情况下重新创建索引,这在大型表上很慢。 (更改规则包括:允许访客发布消息或允许用户删除其帐户,尽管已发布评论等)。

  2. 通过将更改推送到生产存储库,更改数据库并不像部署软件修复程序那么容易。我们希望尽可能避免更改数据库结构。数据库中的业务逻辑越多,您就越需要更改数据库(并触发重新索引)。

  3. TDD。在单元测试中,您可以将数据库替换为模拟并测试功能。如果数据库中有任何业务逻辑,那么您没有进行完整的测试,需要使用数据库进行测试,或者在代码中复制业务逻辑以进行测试,复制逻辑并增加逻辑中不可用的逻辑。同样的方式。

  4. 将您的逻辑重用于不同的数据源。如果数据库中没有逻辑,我的应用程序可以从数据库中的记录创建对象,从Web服务,json文件或任何其他源创建它们。我只需要换掉数据映射器实现,并可以将所有业务逻辑与任何源一起使用。如果数据库中存在逻辑,则这是不可能的,您必须在数据映射器层或业务逻辑中实现逻辑。无论哪种方式,您都需要在代码中进行这些检查。如果没有逻辑数据库我可以使用不同的数据库或平面文件实现在不同的位置部署应用程序。


其它参考13


我同意之前的答案,因为它们对于保持数据一致性很有用。然而,几周前Jeff Atwood发表了一篇有趣的文章,讨论了规范化和一致性数据的优缺点。 [24]


简而言之,在处理大量数据时,非规范化数据库可以更快;并且您可能不关心应用程序的精确一致性,但它会迫使您在处理数据时要小心,因为DB不会。

其它参考14


根据我的经验,最好避免在数据库关键应用程序中使用FK。我不同意这里的人说FKs是一个很好的做法,但是在数据库庞大且具有巨大的CRUD操作/秒的情况下它不实用。我可以在没有命名的情况下分享...在数据库中没有单一FK的最大投资银行之一。这些约束由程序员在创建涉及DB的应用程序时处理。基本原因是当新的CRUD完成时它具有实现多个表并验证每个插入/更新,虽然这对于影响单行的查询不会是一个大问题但是当你处理任何大银行作为日常任务必须做的批处理时,它确实会产生巨大的延迟。


最好避免使用FK,但其风险必须由程序员处理。

其它参考15


我只知道Oracle数据库,没有其他数据库,我可以说外键对于维护数据完整性至关重要。在插入数据之前,需要建立数据结构,并使其正确。完成后 - 因此创建了所有主要和外键 - 工作就完成了!


含义:孤立的行?不,从来没有见过我的生活。除非一个坏程序员忘记了外键,或者他是否在另一个级别实现了这个。两者都是 - 在Oracle的背景下 - 巨大的错误,这将导致数据重复,孤立数据,从而:数据损坏。我无法想象没有强制执行FK的数据库。对我来说这看起来很混乱。这有点像Unix权限系统:想象一下,每个人都是root用户。想想混乱。


外键是必不可少的,就像主键一样。这就像是说:如果我们删除主键怎么办?好吧,完全混乱就会发生。那就是这样。您不能将主键或外键键移动到编程级别,它必须位于数据级别。


缺点 ?是的,一点没错 !因为在插入时,会发生更多的检查。但是,如果数据完整性比性能更重要,那就太明白了.Oracle的性能问题与PK和FK的索引更相关。

其它参考16


它们可以使删除记录更加繁琐 - 你不能删除其他表中有外部密钥违反该约束的记录的主记录。你可以使用触发器进行级联删除。


如果您不明智地选择了主键,那么更改该值会变得更加复杂。例如,如果我将客户表的PK作为人名,并在订单表中将该密钥设为FK,如果客户想要更改其名称,那么这是一种皇家的痛苦......但这只是伪劣的数据库设计。


我相信使用fireign键的优势超过任何所谓的缺点。

其它参考17


验证外键约束需要一些CPU时间,因此有些人省略了外键以获得一些额外的性能。

其它参考18


Clarify数据库是没有主键或外键的商业数据库的示例。


http://www.geekinterview.com/question_details/18869[25]


有趣的是,技术文档很大程度上解释了表是如何相关的,用于连接它们的列等等。


换句话说,他们可以使用显式声明(DRI)加入表格,但选择不


因此,Clarify数据库充满了不一致性,并且表现不佳。


但我认为它使开发人员的工作变得更容易,而不必编写代码来处理引用完整性,例如在删除,添加之前检查相关的行。


我认为,这是在关系数据库中没有外键约束的主要好处。这使得开发更容易,至少从魔鬼可能的关注角度来看。

其它参考19


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

其它参考20


我听到的论点是前端应该有这些业务规则。当你不应该允许任何插入破坏你的约束时,外键增加了不必要的开销。我是否同意这一点?不,但这是我一直听到的。


编辑:我的猜测是他指的是外键约束,而不是外键作为概念。

其它参考21


对我来说,如果你想遵守ACID标准,那么拥有外键以确保参照完整性至关重要。[26]

其它参考22


我必须在这里提出大部分意见,外键是必要的项目,以确保您拥有完整的数据。 ON DELETE和ON UPDATE的不同选项将允许您解决人们在此提及的有关其使用的一些跌倒。


我发现在我所有项目的99%中,我将使用FK来强制执行数据的完整性,但是,在极少数情况下,我有客户端必须保留旧数据,无论它有多糟糕。 ..但是我花了很多时间编写代码,只是为了得到有效的数据,所以它变得毫无意义。

其它参考23


应用程序生命周期的可维护性和稳定性如何?大多数数据的使用寿命都比使用它的应用程序长。关系和数据完整性非常重要,希望下一个开发团队能够在应用程序代码中正确使用它。如果你有对于没有尊重自然关系的脏数据的数据库,你会这样做。数据完整性的重要性将变得非常明确。

其它参考24


我还认为外键在大多数数据库中都是必需的。唯一的缺点(除了强制一致性带来的性能影响)是拥有外键允许人们编写假设有功能外键的代码。永远不应该允许这样做。


例如,我已经看到人们编写插入引用表的代码,然后尝试插入引用表而不验证第一个插入是否成功。如果以后删除了外键,则会导致数据库不一致。


您也没有选择在更新或删除时采取特定行为。无论是否存在外键,您仍然需要编写代码来执行您想要的操作。如果您认为删除不是级联您的删除操作将失败。如果您认为引用列的更新未被引用到引用行,则更新将失败。出于编写代码的目的,您可能也没有这些功能。


如果打开了这些功能,那么无论如何你的代码都会模仿它们,你会失去一点性能。


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

其它参考25


我回应德米特里的答案 - 非常好。


对于那些担心FK经常带来的性能开销的人来说,有一种方法(在Oracle中)可以获得FK约束的查询优化器优势,而无需在插入,删除或更新期间进行约束验证的成本开销。这是使用RELY DISABLE NOVALIDATE属性创建FK约束。这意味着查询优化器假定在构建查询时已强制执行约束,而数据库实际上并未强制执行约束。当你用这样的FK约束填充表时,你必须非常小心地承担责任,以确保你的FK列中没有违反约束的数据,就像你这样做了可能会从涉及此FK约束所在的表的查询中获得不可靠的结果。


我通常在我的数据集市模式中的某些表上使用此策略,但不在我的集成登台模式中。我确保我正在复制数据的表已经强制执行相同的约束,或者ETL例程强制执行约束。

其它参考26


许多在这里回答的人都太依赖于通过参考约束实现参照完整性的重要性。使用引用完整性处理大型数据库的效果不佳。 Oracle在级联删除方面似乎特别糟糕。我的经验法则是应用程序永远不应该直接更新数据库,而应该通过存储过程。这使代码库保持在数据库中,并意味着数据库保持其完整性。


在许多应用程序可能正在访问数据库的情况下,由于引用完整性约束而出现问题,但这归结为控制。


还有一个更广泛的问题,应用程序开发人员可能有非常不同的要求,数据库开发人员可能不一定熟悉。

其它参考27


如果你绝对确定,将来一个底层数据库系统不会改变,我会使用外键来确保数据的完整性。


但这是另一个非常好的现实生活理由,根本不使用外键:


您正在开发一个应该支持不同数据库系统的产品。


如果您正在使用能够连接到许多不同数据库系统的实体框架,您可能还需要支持开源免费无服务器数据库。并非所有这些数据库都支持您的外键规则(更新,删除行......)。


这会导致不同的问题:


1.)在创建或更新数据库结构时,您可能会遇到错误。也许只会出现无提示错误,因为数据库系统会忽略您的外键。


2.)如果您依赖外键,您可以在业务逻辑中进行更少甚至没有数据完整性检查。现在,如果新数据库系统不支持这些外键规则或只是以不同的方式运行,则必须重写业务逻辑。


您可能会问:谁需要不同的数据库系统?好吧,并不是每个人都能负担得起或希望在他的机器上使用完整的SQL Server。这是需要维护的软件。其他人已经在其他数据库系统中投入了时间和金钱。无服务器数据库非常适合只有一台机器的小客户。


没有人知道,所有这些数据库系统的行为如何,但是您的业务逻辑与完整性检查始终保持不变。

其它参考28


我一直认为不使用它们是懒惰的。我被教导应该永远这样做。但后来,我没有听过乔尔的讨论。他可能有充分的理由,我不知道。

其它参考29


FK可能会导致问题的一次是,即使您不再希望密钥可用,也会有参考密钥的历史数据(在查找表中)。

显然,解决方案是预先设计更好的东西,但我在考虑现实世界的情况,你不能总是控制完整的解决方案。

例如:也许您有一个查找表customer_type列出了不同类型的客户 - 假设您需要删除某种客户类型,但(由于业务限制)无法更新客户端软件,并且没有人在开发软件时调用这种情况,即使你知道引用它的历史数据是无关紧要的,它在某些其他表中的外键这一事实可能会阻止你删除该行。

在被焚烧几次后,你可能会偏离数据库执行关系。

(我并不是说这很好 - 只是说明为什么你可以决定一般避免FK和db限制)