我记得在Joel Spolsky听到podcast 014提到他几乎没用过外键(如果我没记错的话)。但是,对我而言,在整个数据库中避免重复和后续数据完整性问题似乎非常重要。
人们有一些坚实的理由为什么(避免与Stack Overflow原则一致的讨论)?
使用外键的原因:
不使用外键的原因:
我认为(我不确定!)大多数已建立的数据库提供了一种指定未强制执行的外键的方法,并且只是一些元数据。由于非执法部门消除了不使用FK的所有理由,如果第二部分中的任何原因适用,您应该走这条路线。
没有充分的理由不使用它们......除非孤立的行对你来说不是什么大问题。
更大的问题是:你会带着眼罩开车吗?如果你开发一个没有参考约束的系统,那就是这样。请记住,业务需求变化,应用程序设计更改,代码更改中的相应逻辑假设,逻辑本身可以重构,等等。一般而言,数据库中的约束在当前逻辑假设下实施,对于特定的逻辑断言和假设集似乎是正确的。
在应用程序的生命周期中,引用和数据检查通过应用程序限制警察数据收集,特别是当新要求推动逻辑应用程序更改时。
对于此列表的主题 - 外键本身并不“改善性能”,也不会从实时事务处理系统的角度显着“降低性能”。但是,在高容量“批量”系统中存在约束检查的聚合成本。所以,这是差异,实时与批量交易过程;批处理 - 通过约束检查产生的经过处理的批次处理的成本受到影响。
在设计良好的系统中,数据一致性检查将在“处理批次之前”完成(尽管如此,此处也存在相关成本);因此,在加载时间内不需要外键约束检查。实际上,在处理批处理之前,应暂时禁用所有约束(包括外键)。
查询性能 - 如果表在外键上连接,则应认识到外键列未被索引(尽管相应的主键按定义编制索引)。通过索引外键,就此而言,通过索引任何键,并在索引上连接表有助于提高性能,而不是通过加入具有外键约束的非索引键。
更改主题,如果数据库只是支持网站显示/呈现内容/等并记录点击次数,那么对所有表格具有完全约束的数据库就会被用于此类目的。想一想。大多数网站甚至不使用数据库。对于类似的要求,只是记录数据而不是按照说法引用数据,请使用没有约束的内存数据库。这并不意味着没有数据模型,是逻辑模型,而是没有物理数据模型。
使用外键的其他原因: - 允许更多地重用数据库
不使用外键的其他原因: - 您正试图通过减少重用来锁定客户的工具。
根据我的经验,最好避免在数据库关键应用程序中使用FK。我不同意这里的人说FKs是一个很好的做法,但是在数据库庞大且具有巨大的CRUD操作/秒的情况下它不实用。我可以在没有命名的情况下分享...最大的投资银行之一在数据库中没有单一的FK。这些约束由程序员在创建涉及DB的应用程序时处理。基本原因是,当新的CRUD完成时,它必须影响多个表并验证每个插入/更新,尽管这对于影响单行的查询不会是一个大问题,但是当您处理时它确实会产生巨大的延迟任何大银行必须做的批处理作为日常任务。
它更好地避免FK,但其风险必须由程序员处理。
“在添加记录之前,检查另一个表中是否存在相应的记录”是业务逻辑。
以下是您在数据库中不希望出现这种情况的一些原因:
我同意之前的答案,因为它们对于保持数据一致性很有用。然而,几周前有一个interesting post by Jeff Atwood讨论了规范化和一致数据的优缺点。
简而言之,在处理大量数据时,非规范化数据库可以更快;并且您可能不关心精确的一致性,具体取决于应用程序,但它会强制您在处理数据时更加小心,因为数据库不会。
我只知道Oracle数据库,没有其他数据库,我可以说外键对于维护数据完整性至关重要。在插入数据之前,需要进行数据结构,并使其正确。完成后 - 因此创建了所有主要和外键 - 工作就完成了!
含义:孤立的行?没有。在我的生活中从未见过。除非一个坏程序员忘记了外键,或者他在另一个级别实现了这个。两者都是 - 在Oracle的背景下 - 巨大的错误,这将导致数据重复,孤立数据,从而:数据损坏。我无法想象没有强制执行FK的数据库。这看起来像是混乱。这有点像Unix权限系统:想象每个人都是root用户。想想混乱。
外键是必不可少的,就像主键一样。这就像是说:如果我们删除主键怎么办?好吧,彻底的混乱将会发生。那是什么。您不能将主键或外键的责任移到编程级别,它必须位于数据级别。
缺点 ?是的,一点没错 !因为在插入时,将会发生更多的检查。但是,如果数据完整性比性能更重要,那么这是一个明智的选择。 Oracle上的性能问题与PK和FK的索引更相关。
它们可以使删除记录更加繁琐 - 您无法删除“主”记录,其中有外键会违反该约束的其他表中的记录。您可以使用触发器进行级联删除。
如果您不明智地选择了主键,那么更改该值会变得更加复杂。例如,如果我将“客户”表的PK作为人名,并在“订单”表中将该密钥设为FK,“如果客户想要更改其名称,那么这是一个皇家的痛苦......但这只是伪劣的数据库设计。
我相信使用外键的优势超过任何所谓的缺点。
验证外键约束需要一些CPU时间,因此有些人省略了外键以获得一些额外的性能。
Clarify数据库是没有主键或外键的商业数据库的示例。
http://www.geekinterview.com/question_details/18869
有趣的是,技术文档很大程度上解释了表是如何相关的,用于连接它们的列等等。
换句话说,他们可以使用显式声明(DRI)加入表格,但他们选择不这样做。
因此,Clarify数据库充满了不一致性,并且表现不佳。
但我认为它使开发人员的工作变得更容易,而不必编写代码来处理引用完整性,例如在删除,添加之前检查相关的行。
我认为,这是在关系数据库中没有外键约束的主要好处。这使得开发更容易,至少从恶魔般的关注角度来看。
这是一个培养问题。如果你在教育或职业生涯的某个地方花时间喂养和照顾数据库(或者与有才能的人一起密切合作),那么实体和关系的基本原则在你的思维过程中根深蒂固。其中的基础是如何/何时/为什么在数据库中指定密钥(主要,外部和备用)。这是第二天性。
但是,如果您在过去与RDBMS相关的工作中没有那么彻底或积极的经验,那么您可能没有接触到这些信息。或许你的过去包括沉浸在一个大声反数据库的环境中(例如,“那些DBA是白痴 - 我们很少,我们选择了几个java / c#代码甩卖者将节省一天”),在这种情况下你可能会强烈反对对于一些dweeb的神秘唠叨告诉你,如果你只是倾听,FK(以及他们可以暗示的限制)真的很重要。
大多数人都是在他们还是小孩的时候被教过,刷牙很重要。你可以没有它吗?当然,但是如果你在每顿饭后刷过,那么在某个地方你可以获得的牙齿比你可以拥有的牙齿少。如果妈妈和爸爸有足够的责任来涵盖数据库设计和口腔卫生,我们就不会进行这种对话。 :-)
我也听过这个论点 - 那些忘记在外键上放一个索引然后抱怨某些操作很慢的人(因为约束检查可以利用任何索引)。总结一下:没有充分的理由不使用外键。所有现代数据库都支持级联删除,因此......
我听到的论点是前端应该有这些业务规则。当你不应该允许任何插入破坏你的约束时,外键“增加了不必要的开销”。我同意这个吗?不,但这是我一直听到的。
编辑:我的猜测是他指的是外键约束,而不是外键作为一个概念。
对我来说,如果你想遵循ACID标准,那么拥有外键以确保参照完整性至关重要。
我必须在这里提出大部分评论,外键是必要的项目,以确保您拥有完整的数据。 ON DELETE和ON UPDATE的不同选项将允许您解决人们在此提及的有关其使用的一些“跌倒”。
我发现在我所有项目的99%中,我将使用FK来强制执行数据的完整性,但是,在极少数情况下,我有客户必须保留他们的旧数据,无论它有多糟糕......但是后来我花了很多时间编写代码来获取有效数据,所以它变得毫无意义。
应用程序生命周期的可维护性和稳定性如何?大多数数据的使用寿命都比使用它的应用程序长。关系和数据完整性非常重要,希望下一个开发团队能够在应用程序代码中正确使用它。如果您没有使用不尊重自然关系的脏数据来处理数据库,那么您将会这样做。数据完整性的重要性将变得非常明确。
我还认为外键在大多数数据库中都是必需的。唯一的缺点(除了强制一致性带来的性能影响)是拥有外键允许人们编写假设有功能外键的代码。永远不应该允许这样做。
例如,我看到人们编写插入引用表的代码,然后尝试插入引用表而不验证第一个插入是否成功。如果以后删除了外键,则会导致数据库不一致。
您也没有选择在更新或删除时假定特定行为。无论是否存在外键,您仍然需要编写代码来执行您想要的操作。如果您认为删除不是级联,则删除将失败。如果您假定引用列的更新未引用到引用行,则更新将失败。出于编写代码的目的,您可能也没有这些功能。
如果启用了这些功能,那么您的代码无论如何都会模拟它们,您将失去一点性能。
因此,摘要....如果您需要一致的数据库,外键是必不可少的。永远不要假定外键在您编写的代码中存在或起作用。
我回应德米特里的答案 - 非常好。
对于那些担心FK经常带来的性能开销的人来说,有一种方法(在Oracle中)可以获得FK约束的查询优化器优势,而无需在插入,删除或更新期间进行约束验证的成本开销。这是使用RELY DISABLE NOVALIDATE属性创建FK约束。这意味着查询优化器确定在构建查询时已强制执行约束,而数据库实际上并未强制执行约束。当您使用这样的FK约束填充表格时,您必须非常小心地承担责任,以确保您的FK列中没有违反约束的数据,就像您这样做了可能会从涉及此FK约束所在的表的查询中获得不可靠的结果。
我通常在我的数据集市架构中的某些表上使用此策略,但不在我的集成登台模式中使用。我确保我正在复制数据的表已经强制执行相同的约束,或者ETL例程强制执行约束。
许多在这里回答的人都太依赖于通过参考约束实现参照完整性的重要性。使用引用完整性处理大型数据库效果不佳。 Oracle在级联删除方面似乎特别糟糕。我的经验法则是应用程序不应该直接更新数据库,而应该通过存储过程。这使代码库保持在数据库中,并意味着数据库保持其完整性。
在许多应用程序可能正在访问数据库的情况下,由于引用完整性约束而出现问题,但这归结为控制。
还有一个更广泛的问题,应用程序开发人员可能有非常不同的要求,数据库开发人员可能不一定熟悉。
如果你完全确定,将来一个底层数据库系统不会改变,我会使用外键来确保数据的完整性。
但这是另一个非常好的现实生活理由,根本不使用外键:
您正在开发一个应该支持不同数据库系统的产品。
如果您正在使用能够连接到许多不同数据库系统的实体框架,您可能还需要支持“开源免费”无服务器数据库。并非所有这些数据库都支持您的外键规则(更新,删除行......)。
这可能会导致不同的问题:
1.)创建或更新数据库结构时,您可能会遇到错误。也许只会出现无提示错误,因为数据库系统只会忽略您的外键。
2.)如果您依赖外键,您可以在业务逻辑中进行更少甚至没有数据完整性检查。现在,如果新数据库系统不支持这些外键规则或只是以不同的方式运行,则必须重写业务逻辑。
您可能会问:谁需要不同的数据库系统?好吧,并不是每个人都能买得起或想要在他的机器上使用完整的SQL Server。这是需要维护的软件。其他人已经在其他数据库系统中投入了时间和金钱。无服务器数据库非常适合只有一台机器的小客户。
没有人知道,所有这些数据库系统的行为如何,但是您的业务逻辑与完整性检查始终保持不变。
我一直认为不使用它们是懒惰的。我被教导应该永远这样做。但后来,我没有听过乔尔的讨论。他可能有充分的理由,我不知道。
我确信有很多应用程序可以让你逃脱它,但这不是最好的主意。您不能总是指望您的应用程序正确管理您的数据库,坦率地管理数据库不应该是您的应用程序非常关注。
如果您使用的是关系数据库,那么您似乎应该在其中定义一些关系。不幸的是,这种态度(你不需要外键)似乎被许多应用程序开发人员所接受,他们宁愿不被数据完整性等愚蠢的事情所困扰(但需要因为他们的公司没有专门的数据库开发人员)。通常在这些类型的数据库中,你很幸运只有主键;)
有一次当FK可能导致您出现问题时,即使您不再希望密钥可用,您的历史数据也会引用该密钥(在查找表中)。
显然,解决方案是预先设计好的东西,但我在考虑现实世界的情况,你并不总是能控制完整的解决方案。
例如:也许您有一个查找表customer_type
列出了不同类型的客户 - 假设您需要删除某个客户类型,但(由于业务限制)无法更新客户端软件,并且没有人访问此类在开发软件的情况下,即使您知道引用它的历史数据是无关紧要的,它在某些其他表中的外键这一事实可能会阻止您删除该行。
在被焚烧几次之后,你可能会远离数据库执行关系。
(我不是说这很好 - 只是说明为什么你可以决定一般避免FK和db限制)
外键对于任何关系数据库模型都是必不可少的。
我总是使用它们,但后来我为金融系统制作数据库。数据库是应用程序的关键部分。如果财务数据库中的数据不完全准确,那么您在代码/前端设计中花费了多少精力并不重要。你只是在浪费时间。
还有一个事实是,多个系统通常需要直接与数据库接口 - 从刚读取数据的其他系统(Crystal Reports)到插入数据的系统(不一定使用我设计的API;它可能由一个刚刚发现VBScript且具有SQL框SA密码的愚蠢的经理。如果数据库不像它可能的那样白痴,那么再见数据库。
如果您的数据很重要,那么请使用外键,创建一组存储过程来与数据交互,并创建最难的数据库。如果您的数据不重要,为什么要开始使用数据库?
更新:我现在总是使用外键。我对“他们复杂测试”的反对意见的回答是“编写你的单元测试,以便他们根本不需要数据库。任何使用数据库的测试都应该正确使用它,包括外键。如果设置很痛苦,找到一种不太痛苦的方法进行设置。“
假设您正在使用外键。您正在编写一个自动化测试,上面写着“当我更新金融帐户时,它应该保存交易记录。”在这个测试中,你只关心两个表:accounts
和transactions
。
然而,accounts
有contracts
的外键,contracts
有qk到clients
,clients
有fk到cities
,而cities
有fk到states
。
现在,如果没有在与测试无关的四个表中设置数据,数据库将不允许您运行测试。
至少有两种可能的观点:
也可以在运行测试时暂时关闭外键检查。 MySQL,至少,supports this。
@imphasing - 这正是那种导致维护噩梦的心态。
为什么哦,为什么你会忽略声明性引用完整性,其中数据可以保证至少是一致的,有利于所谓的“软件实施”,这是一种最好的弱预防措施。
“他们可以删除记录更加繁琐 - 你不能删除”主“记录,其他表中有外键会违反该约束的记录。”
重要的是要记住,SQL标准定义了删除或更新外键时所采取的操作。我所知道的是:
ON DELETE RESTRICT
- 防止删除另一个表中包含此列中键的任何行。这就是Ken Ray上面描述的内容。ON DELETE CASCADE
- 如果删除了另一个表中的行,请删除此表中引用它的所有行。ON DELETE SET DEFAULT
- 如果删除了另一个表中的行,请将引用它的任何外键设置为列的默认值。ON DELETE SET NULL
- 如果删除了另一个表中的行,请将此表中引用它的任何外键设置为null。ON DELETE NO ACTION
- 这个外键只标志它是外键;即用于OR映射器。这些相同的行动也适用于ON UPDATE
。
默认值似乎取决于您使用的sql服务器。
有一个很好的理由不使用它们:如果你不理解它们的作用或如何使用它们。
在错误的情况下,外键约束可能导致事故的瀑布复制。如果有人删除了错误的记录,撤消它可能会成为一项巨大的任务。
而且,相反,当你需要移除某些东西时,如果设计不当,约束会导致各种阻止你的锁。