外键怎么了?


259

我记得在播客014中听到乔尔·斯波尔斯基(Joel Spolsky)提到他几乎从未使用过外键(如果我没记错的话)。但是,对我来说,它们对于避免整个数据库中的重复和后续数据完整性问题至关重要。

人们为什么会有一些扎实的理由(避免按照堆栈溢出原则进行讨论)?

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


9
我不认为Joel不使用FK,只是他没有让数据库强制执行它们。从逻辑上讲,它们仍然是FK!
Daren Thomas

6
他说他不使用外键,但我同意达伦的观点,他的意思是他不使用外键CONSTRAINTS。一个表中的一列,其值应从另一个表的主键/唯一键中获取,无论您是否添加约束,它们都是外键。
托尼·安德鲁斯

22
...通常,不添加约束是愚蠢的:即使在应用程序代码中存在错误,或者即使您在后台进行数据“修复”,也始终确保完整性。
托尼·安德鲁斯

2
+1对于Tony的评论。功能与外键的逻辑概念之间存在太多混淆。
JohnFx

4
@DanMan,我不知道您从哪里得到的印象。我实际上在上面说过:“通常,不添加约束是愚蠢的:它始终确保完整性”
Tony Andrews

Answers:


352

使用外键的原因:

  • 你不会得到孤行
  • 您可以获得很好的“删除级联”行为,自动清理表
  • 了解数据库中表之间的关系有助于优化器计划查询以实现最高效的执行,因为它可以更好地估计连接基数。
  • FK可以很大程度地暗示哪些最重要的统计信息需要从数据库中收集,从而提高性能
  • 它们支持各种自动生成的支持-ORM可以自行生成,可视化工具将能够为您创建漂亮的架构布局,等等。
  • 该项目的新手将更快地进入流程,因为否则会明确记录隐式关系

不使用外键的原因:

  • 将使数据库在每个CRUD操作上都变得多余,因为它必须检查FK一致性。如果您经常流失,这可能是一笔不小的费用
  • 通过执行关系,FK可以指定必须添加/删除内容的顺序,这可能导致DB拒绝执行您想要的操作。(在这种情况下,可以尝试创建一个孤立行,这通常不是一件好事)。当您进行大批量更新时,这尤其痛苦,当您先加载一个表又加载另一个表时,第二个表将创建一致的状态(但是如果第二次加载失败并且您的表数据库现在不一致?)。
  • 有时您事先知道您的数据将变得很脏,您接受了它,并且希望数据库接受它
  • 你只是懒惰:-)

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


12
好名单!DBM不会检查CRUD的“ R”部分的一致性,因此我将其删除。另外,这可能是一种洗礼,因为在您的应用程序中,您执行DBMS的相同操作:您将检查并确保父ID在CRD之前有效,这实际上比让DBM慢!
Matt Rogish

6
如果在您插入孩子时有人删除了父母该怎么办?现在,当我提交“添加评论”时-如果您已经删除了答案,则此评论现在是孤立的。FK会阻止它。另外,我可以将parentID更改为我想要的任何内容。有人需要检查。:)
Matt Rogish

7
准确地说,这应该是数据库的工作,因为它是唯一可以在面对多个并发客户端时保证事务性的工作。
SquareCog

3
+1出色答卷-第二个理由不使用FK约束可以被认为是“就更难突破的一致性”,这实际上听起来像一个东西!
比尔·卡文

9
在我看来,使用外键FAR的好处胜过不使用外键的任何好处。
尼克·贝德福德

80

这是一个成长的问题。如果您在教育或职业生涯中的某个地方花了一些时间来喂养和维护数据库(或与做过这项工作的有才华的人紧密合作),那么实体和关系的基本原则在您的思考过程中就根深蒂固。在这些基础知识中,包括如何/何时/为什么在数据库中指定键(主键,外键以及备用键)。这是第二天性。

但是,如果您过去在RDBMS相关工作中没有获得如此全面或积极的经验,那么您可能就不会接触到此类信息。或者,您的过去可能包括沉浸在一个反数据库的环境中(例如,“那些DBA是白痴-我们很少,我们选择了很少的Java / c#代码吊销员可以节省一天的时间”),在这种情况下,您可能会强烈反对听到一些dweeb的神秘语,告诉您FK(以及它们可能暗示的约束)确实很重要,如果您只想听的话。

多数人从小就被教导刷牙很重要。没有它,你能渡过难关吗?当然可以,但是在某些地方,您的牙齿供应比每顿饭后刷牙要少。如果父母有足够的责任涵盖数据库设计以及口腔卫生,那么我们就不会进行对话。:-)


61
我将使用经过提炼的“外键就像刷牙:继续前进,不要使用它,但是微笑时要小心”
Mark Sowul 2012年

5
我个人发现RDBMS'的原理比口腔卫生的原理更简单,定义更明确
Ali Gangji 2014年

未来10年,我肯定会与我的儿子/女儿进行这种数据库设计讨论,这样他/她就不会搞砸了,最终不会因为数据库问题而成为下一次华尔街崩溃的原因。
VarunAgw

52

我敢肯定,有很多应用程序可以解决这个问题,但这不是最好的主意。您不能总是依靠您的应用程序来正确地管理数据库,并且坦率地说,管理数据库对您的应用程序不是很重要。

如果您正在使用关系数据库,那么似乎应该在其中定义一些关系。不幸的是,许多应用程序开发人员似乎都接受了这种态度(不需要外键),他们宁愿不理会诸如数据完整性之类的愚蠢事物(但需要这样做,因为他们的公司没有专门的数据库开发人员)。通常在由这些类型组合在一起的数据库中,只有主键才很幸运;)


26
我真的没有那些数据库中没有FK的人。我上一次与没有它的人一起工作时,他说:“不,我们在应用程序中强制执行。” 除了我对所有客户数据库进行的调查之外,发现其中大多数确实有孤儿...
ErikE 2010年

1
通常情况就是这样。我认为您可以只对数据库执行强制操作(只要您的用户不介意运行时异常),但是同时拥有这两种方法实际上是唯一的方法。
AlexCuse

一切都在笔录/ Atwood的回答中:“ Atwood:...根据您在索引中设置的外键,他们会发现... Spolsky:[笑]假设您这样做了。Atwood:嗯,假设您可以正确设置数据库...”
MemeDeveloper 2013年

4
数据库不叫关系,因为的关系表之间(每一种数据库具有某种实体之间的关系的!),但由于表本身的关系,在数学方面。参见维基百科
Massimiliano Kraus,

41

外键对于任何关系数据库模型都是必不可少的


54
模型,是的。该实现不是必需的,只是可能有用。
dkretz'3

4
抱歉,但是应用程序开发人员不更广泛使用对象数据库管理系统(又名NoSQL数据库!)的主要原因是由于对RDBMS的投资。大多数时候,数据库(不是数据库管理系统)是中间层对象模型,通常涉及分布式缓存。无论如何,这是删除级联,所有权和更改同步的地方。RDBMS主要用于持久化此对象模型,通常是经过艰苦而实际上毫无价值的ORM练习之后。关系模型在大多数时候是没有必要的!
Sentinel 2012年

2
不,不是必须强制使用外键来表示“关系”
Silver Moon

这实际上并不能解释太多。

29

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

还有一个事实是,多个系统通常需要直接与数据库进行交互-从仅读取数据的其他系统(Crystal Reports)到插入数据的系统(不一定使用我设计的API;它可能是由笨拙的经理,他刚刚发现了VBScript并具有SQL框的SA密码。如果数据库不尽如人意,那么-再见数据库。

如果您的数据很重要,那么可以,使用外键,创建一套存储过程来与数据进行交互,并制作出最坚固的数据库。如果您的数据不重要,那么为什么要建立一个数据库呢?


2
很好的见识。我认为数据对于每个实际使用的应用程序都至关重要。唯一不同的是数据损坏的后果。它们非常适合您的应用……
杰伊·戈德斯

20

更新:我现在总是使用外键。对于“他们的复杂测试”的异议,我的回答是“编写单元测试,这样他们根本不需要数据库。使用该数据库的任何测试都应正确使用它,并包括外键。如果设置很麻烦,找到一种更轻松的方法来进行设置。”


外键使自动测试复杂化

假设您正在使用外键。您正在编写一个自动测试,内容为“当我更新财务帐户时,它应该保存交易记录”。在此测试中,您只关心两个表:accountstransactions

但是,accounts具有的外键contractscontracts具有的fk clientsclients具有的fk citiescities具有的fk states

现在,如果没有在与测试无关的四个表中设置数据,数据库将不允许您运行测试

对此至少有两种可能的观点:

  • “这是一件好事:您的测试应该是现实的,并且那些数据约束将存在于生产中。”
  • “这是一件坏事:您应该能够对系统的各个部分进行单元测试,而无需涉及其他部分。您可以为整个系统添加集成测试。”

在运行测试时,也可能暂时关闭外键检查。至少MySQL 支持此功能


我发现自己通常在这里走中间路线:我使用FK,然后编写用于设置数据库以支持各种测试方案的单元测试助手方法,例如,用于填充任何测试的“城市”和“州”的助手方法。需要填充这些表。
joelpt 2014年

您可能应该在不相关实体之间使用链接表。或者走得更远-单独的DBS:考虑面向服务的体系结构或微服务中的情况,其中每个元素(客户,帐户,交易)都是不同的系统,具有不同的DB。它们之间完全没有FK。在这种情况下,应该使用FK来防止每种数据类型的子表中的孤立数据。
JeeBee 2014年

3
还有一些DBMS允许延迟约束,以便仅在提交整个事务时才检查约束,因此插入,更新,删除的顺序无关紧要
a_horse_with_no_name 2014年

2
如果要从业务层测试更新,则开发环境应具有FK。更新记录时,应具有成功更新所需的列值。否则,恕我直言,您的测试无效。
KeyOfJ

3
您的数据库甚至不应该参与单元测试,应该对它们进行模拟。在集成测试中会涉及到它们,但是除非您修复它,否则由于外键而导致的任何问题都是您的用户也会遇到的问题。
AndreasBergström'17

16

“它们会使删除记录变得更加麻烦-如果其他表中有外键违反该约束的记录,则无法删除“主”记录。”

重要的是要记住,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

默认值似乎取决于哪个 您正在使用的服务器。


14

@强调-这正是导致维护噩梦的心态。

为什么?为什么您会忽略声明性参照完整性(可以保证数据至少是一致的),而采用所谓的“软件强制实施”,这充其量是一个较弱的预防措施。


因为涉及的开发人员从未解决过需要非平凡,规范化,关系模型的问题。很多问题都没有,尤其是当今流行的Web /“社交媒体”类型编程中充斥的那种问题。如果使ORM框架后端产生的问题满足了alpha的问题,那么任何人都不可能对数据建模有更多的思考。K / V商店,文档数据库或直接对象序列化都可以轻松解决许多此类问题。
zxq9

12

不使用它们有一个很好的理由: 如果您不了解它们的作用或使用方法。

在错误的情况下,外键约束可能会导致意外瀑布式复制。如果有人删除了错误的记录,那么将其撤消将成为一项艰巨的任务。

同样,相反地,当您需要删除某些东西时,如果设计不当,约束可能会导致各种阻止您的锁。


8
在不备份的情况下删除生产中的行不是有效的参数。如果您不理解它们,则应该考虑学习而不是忽略它。
纪尧姆

2
@Guillaume我认为他的回答有点讽刺,不要从字面上看:如果您不理解它们,那就不要使用它们。但是,当然您应该同时理解和使用它们。
本杰明

^这。它们是有用的工具,但在新手手中,它们是危险的工具。
肯特·弗雷德里克

11

没有充分的理由使用它们……除非我认为孤立行对您来说没什么大不了的。


11
为什么孤立行很重要?
Seen Osewa

2
那多线程呢?在某些情况下,它们可能导致多线程噩梦。在具有写入数据库的多个线程的复杂应用程序中,这些线程可能遇到需要相互引用的对象,最好在业务逻辑中控制引用完整性-尤其是如果表随后将变为静态的话。
基思·品森

我同意。另外,我更喜欢让ophan行能够及时恢复,而不是无情地丢弃它们。
PedroD

4

更大的问题是:你会蒙着眼睛开车吗?如果您开发的系统没有参照约束,便会这样。请记住,业务需求发生了变化,应用程序设计发生了变化,代码中相应的逻辑假设发生了变化,逻辑本身可以重构,等等。通常,数据库中的约束是在当代逻辑假设下放置的,似乎对于特定的一组逻辑断言和假设是正确的。

在应用程序的整个生命周期中,引用检查和数据检查会限制通过应用程序收集数据的工作,尤其是在新需求推动逻辑应用程序更改时。

对于此清单的主题 -从实时交易处理系统的角度来看,外键本身并不“改善性能”,也不显着“降低性能”。但是,在HIGH批量“批处理”系统中,用于约束检查的总成本较高。因此,这是实时交易与批处理交易的区别;批处理-顺序处理的批处理的总成本(通过约束检查确保的总成本)会对性能造成影响。

在一个设计良好的系统中,将在处理批处理之前“进行”数据一致性检查(尽管如此,这里也会产生成本);因此,在加载期间不需要进行外键约束检查。实际上,应暂时禁用所有约束(包括外键),直到处理批次为止。

查询性能 -如果在外键上联接表,请注意以下事实:外键列未索引(尽管相应的主键已按定义索引)。为此,通过对外键建立索引,对任何键进行索引,并在已索引的表上连接表有助于提高性能,而不是通过对具有外键约束的未索引键进行联接。

更改主题,如果数据库仅支持网站显示/呈现内容/等并记录点击,则出于这种目的,在所有表上都具有完全约束的数据库已被淘汰。想一想。大多数网站甚至都不使用数据库。对于类似的要求,如果只是说要记录数据而不是每个人都引用数据,请使用没有限制的内存数据库。这并不意味着没有数据模型,是逻辑模型,但是没有物理数据模型。


好吧,我不知道为什么要插入3个双换行符来代替空格并更改两个单词为'67%是Jonathan Leffler',但是我不认为我做了很多类似的工作。正文由@jay提供(用户183837)。
乔纳森·勒夫勒

我只是以为paragrahps无法像大多数其他站点一样在这里工作。因此,我将所有内容一并使用,使用粗体表示流量变化。
贾斯比尔L

3

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

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


3

根据我的经验,最好避免在数据库关键应用程序中使用FK。我不会不同意这里的人,他们说FK是一种很好的做法,但是在数据库庞大且每秒CRUD操作数量巨大的情况下,它是不实际的。我可以不命名而共享...最大的投资银行之一在数据库中没有一个FK。这些约束由程序员在创建涉及DB的应用程序时处理。根本原因是,每当执行新的CRUD时,它都必须影响多个表并为每个插入/更新进行验证,尽管对于影响单行的查询而言,这并不是一个大问题,但当您处理时,它确实会造成巨大的延迟大型银行必须将其作为日常任务来进行的批处理。

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


8
我认为大型银行的开发实践没有设定黄金标准。
Adriaan Koster 2015年

3

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

以下是一些您不希望在数据库中使用的原因:

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

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

  3. TDD。在单元测试中,您可以用数据库代替模拟并测试功能。如果您的数据库中有任何业务逻辑,则您未在进行完整的测试,因此需要使用数据库进行测试或出于测试目的而在代码中复制业务逻辑,从而复制逻辑并增加了逻辑无法在数据库中工作的可能性。同样的方法。

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


2

我同意前面的答案,因为它们对于保持数据一致性很有用。但是,几周前Jeff Atwood发表了一篇有趣的文章,讨论了标准化和一致数据的利弊。

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


杰夫提出了一些好的观点。但是,“ Enterprise Rails”中的Dan Chak展示了一种设计缓存表的方法,该表本质上是数据的非规范化副本。查询运行速度很快,并且如果不必刷新表,它将运行良好。我发现,如果您的数据驱动了应用程序的行为(例如,应用程序状态),则您需要尽可能地对数据进行规范化,因为否则,不一致的数据会导致不一致的应用程序行为。
杰伊·戈德斯

在一致的,预期的访问路径上读取大量数据时,可以使用非规范化数据仓库。在所有其他情况下,这都是危险的谬论。
Peter Wone 2013年

2

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

http://www.geekinterview.com/question_details/18869

有趣的是,技术文档不遗余力地解释了表如何关联,使用哪些列来联接它们等。

换句话说,他们可以将表与显式声明(DRI)联接在一起,但是他们选择不这样做

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

但是我想这使开发人员的工作更加轻松,而不必编写代码来处理参照完整性,例如在删除,添加之前检查相关行。

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


处理失败的参照完整性检查的代码比处理不一致的数据的代码小得多。
杰伊·戈德斯

@杰伊同意!不要以为我提倡这种方法。
Ed Guiness

2

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

含义:孤行?不。我一生中从未见过。除非一个糟糕的程序员忘记了外键,否则他是否要在另一个级别上实现它。在Oracle中,两者都是巨大的错误,这将导致数据重复,孤立数据,从而导致数据损坏。我无法想象没有实施FK的数据库。在我看来,这很混乱。有点像Unix许可系统:想象每个人都是root。想想混乱。

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

缺点 ?是的,一点没错 !因为在插入时,将要进行更多检查。但是,如果数据完整性比性能更重要,那就很容易了。Oracle上的性能问题更多地与PK和FK附带的索引有关。


1

它们会使删除记录变得更加麻烦-如果其他表中有外键违反该约束的记录,则无法删除“主”记录。您可以使用触发器进行级联删除。

如果您不明智地选择了主键,那么更改该值将变得更加复杂。例如,如果我将“客户”表的PK作为该人的名字,并将该键设置为“订单”表中的FK”,那么如果客户要更改其姓名,那就太麻烦了。 。但这只是伪劣的数据库设计。

我相信使用Fireign密钥的优势胜过任何假定的劣势。


5
我倾向于很少删除任何东西。只需标记一个“可见/活动”位即可。
丹娜

为“我相信使用Fireign密钥的优势胜过任何假定的劣势” +1
Ian Boyd

2
您永远都不会更改主键的值。您删除整个行并以不同的方式重新创建它。如果您认为需要更改它,则说明您的模式存在缺陷。
DanMan

如果您在CustomerId(PK)上设置了外键,那么更改客户名称完全不会费劲。在订单表中。唯一的麻烦是,如果FK是在CustomerName上设置的,那就永远不会这样。恕我直言
KeyOfJ

1

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


6
删除重复和不一致的数据要花费多少CPU时间?
Ed Guiness

是的,这是真的。在我正在使用的系统上,我们必须一次将10-40 gigs数据插入数据库中,而FK的性能(无论有没有)在总时间中都是可见的。
Paul Mendoza,2009年

1

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


9
我认为,某些人(从我的角度来看,大多数人未使用FK约束)的真正原因是纯粹的懒惰,假装他们可以用性能节省的理由捍卫自己的懒惰。我坚信,我们公司产生的大部分愚蠢支出是由于缺乏FK约束的强制执行以及由此产生的连锁反应。缺少唯一键是导致2000多个行存储过程,具有12个嵌套IF和随机缩进级别的另一个原因,但现在我要停止。
乍得

1

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

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


不。他不喜欢实际的钥匙!
ljs

令我惊讶。不喜欢外键约束和不喜欢外键之间有很大的区别。我不确定没有它们的关系数据库。
lordscarlet

是的,当我听到他的消息时,我感到震惊。但是,他可能会无意间开玩笑。也许他会在这里发布并在某个阶段进行澄清:-)
ljs

1

对我来说,如果要遵循ACID标准,拥有外键以确保引用完整性至关重要。


1

在这里,我要谈第二大部分的意见,外键是确保您拥有完整数据的必要项。ON DELETE和ON UPDATE的不同选项将使您避开人们在此处提到的有关其使用的“下降”。

我发现在我所有项目的99%中,我将拥有FK来加强数据的完整性,但是,在极少数情况下,无论客户的数据有多糟糕,我都必须保留他们的旧数据。但是后来我花了很多时间编写代码,无论如何都只能获取有效数据,因此变得毫无意义。


1

在整个应用程序生命周期中的可维护性和稳定性如何?大多数数据的寿命比使用该数据的应用程序更长。关系和数据完整性非常重要,以至于无法期望下一个开发团队在应用程序代码中正确实现它。如果您尚未使用不尊重自然关系的脏数据来处理数据库,那么您会这样做。数据完整性的重要性将变得非常明显。


1

我还认为外键在大多数数据库中都是必需的。唯一的缺点(除了具有强制一致性所带来的性能损失外)是拥有外键使人们可以编写假定存在功能性外键的代码。那绝对是不允许的。

例如,我已经看到人们编写代码,将代码插入到被引用的表中,然后在不验证第一个插入成功的情况下尝试将其插入到引用表中。如果以后删除外键,则会导致数据库不一致。

您也不能选择对更新或删除采取特定行为。无论是否存在外键,您仍然需要编写代码来执行所需的操作。如果您假定删除不是级联的,则删除将失败。如果您假设对引用列的更新没有传播到引用行,则更新将失败。为了编写代码,您可能不具备这些功能。

如果启用了这些功能,那么您的代码无论如何都会模拟它们,并且您将失去一些性能。

因此,摘要...。如果您需要一致的数据库,则外键至关重要。切勿假定外键在您编写的代码中存在或起作用。


1

我赞同德米特里的答案-很好。

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

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


1

许多在这里回答问题的人都过于依赖通过引用约束实现的引用完整性的重要性。在具有参照完整性的大型数据库上工作效果不佳。Oracle似乎特别擅长级联删除。我的经验法则是应用程序永远不要直接更新数据库,而应该通过存储过程。这将代码库保留在数据库中,并意味着数据库保持其完整性。

在许多应用程序可能正在访问数据库的地方,由于引用完整性约束的确会出现问题,但这取决于控件。

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


5
“应用程序永远不要直接更新数据库,而应该通过存储过程进行更新。这会将代码库保留在数据库中,并且意味着数据库可以保持其完整性。” <-这里有一个假设,即存储过程中的逻辑不会违反数据完整性,这完全是错误的。
蒂姆·高铁

1

如果您绝对确定一个基础数据库系统将来不会更改,那么我将使用外键来确保数据完整性。

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

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

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

这可能导致不同的问题:

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

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

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

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


0

我一直认为不使用它们是懒惰的。有人告诉我应该总是这样做。但是后来,我没有听乔尔的讨论。我不知道他可能有很好的理由。


尽管也许我应该独立研究他对这个问题的想法,但它更像是在讨论而不是讨论。但是,我也很好奇社区对此主题的看法!
ljs

0

FK可能会导致您出现问题的一种情况是,即使您不再希望该密钥可用,您仍具有(在查找表中)引用该密钥的历史数据。
显然,解决方案是预先设计更好的东西,但是我正在考虑现实世界中的情况,在这种情况下,您并不总是拥有完整解决方案的控制权。
例如:也许您有一个查找表customer_type,其中列出了不同类型的客户-假设您需要删除某种客户类型,但是(由于业务限制)无法更新客户端软件,并且没有人设想这种情况在开发软件时,即使您知道引用该行的历史数据无关紧要,但实际上它是其他表中的外键可能会阻止您删除该行。
在被烧毁了几次之后,您可能会远离数据库关系的实施。
(我并不是说这很好,只是给出一个原因,您可以决定一般避免使用FK和db约束。)


如果我理解您要说的话,我想我的答案是在逻辑上删除查找表中的记录或将不再相关的历史数据存档并也存档查找记录。
乍得2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.