我记得在播客014中听到乔尔·斯波尔斯基(Joel Spolsky)提到他几乎从未使用过外键(如果我没记错的话)。但是,对我来说,它们对于避免整个数据库中的重复和后续数据完整性问题至关重要。
人们为什么会有一些扎实的理由(避免按照堆栈溢出原则进行讨论)?
我记得在播客014中听到乔尔·斯波尔斯基(Joel Spolsky)提到他几乎从未使用过外键(如果我没记错的话)。但是,对我来说,它们对于避免整个数据库中的重复和后续数据完整性问题至关重要。
人们为什么会有一些扎实的理由(避免按照堆栈溢出原则进行讨论)?
Answers:
使用外键的原因:
不使用外键的原因:
我认为(我不确定!)大多数已建立的数据库都提供了一种指定未强制执行的外键的方法,该外键只是一些元数据。由于不执行强制措施会清除所有不使用FK的原因,因此,如果第二部分中的任何原因适用,您可能应该走这条路。
这是一个成长的问题。如果您在教育或职业生涯中的某个地方花了一些时间来喂养和维护数据库(或与做过这项工作的有才华的人紧密合作),那么实体和关系的基本原则在您的思考过程中就根深蒂固。在这些基础知识中,包括如何/何时/为什么在数据库中指定键(主键,外键以及备用键)。这是第二天性。
但是,如果您过去在RDBMS相关工作中没有获得如此全面或积极的经验,那么您可能就不会接触到此类信息。或者,您的过去可能包括沉浸在一个反数据库的环境中(例如,“那些DBA是白痴-我们很少,我们选择了很少的Java / c#代码吊销员可以节省一天的时间”),在这种情况下,您可能会强烈反对听到一些dweeb的神秘语,告诉您FK(以及它们可能暗示的约束)确实很重要,如果您只想听的话。
多数人从小就被教导刷牙很重要。没有它,你能渡过难关吗?当然可以,但是在某些地方,您的牙齿供应比每顿饭后刷牙要少。如果父母有足够的责任涵盖数据库设计以及口腔卫生,那么我们就不会进行对话。:-)
我敢肯定,有很多应用程序可以解决这个问题,但这不是最好的主意。您不能总是依靠您的应用程序来正确地管理数据库,并且坦率地说,管理数据库对您的应用程序不是很重要。
如果您正在使用关系数据库,那么似乎应该在其中定义一些关系。不幸的是,许多应用程序开发人员似乎都接受了这种态度(不需要外键),他们宁愿不理会诸如数据完整性之类的愚蠢事物(但需要这样做,因为他们的公司没有专门的数据库开发人员)。通常在由这些类型组合在一起的数据库中,只有主键才很幸运;)
外键对于任何关系数据库模型都是必不可少的。
我总是使用它们,但是后来我为金融系统创建了数据库。数据库是应用程序的关键部分。如果财务数据库中的数据不完全准确,那么您在代码/前端设计中投入多少精力就无关紧要了。您只是在浪费时间。
还有一个事实是,多个系统通常需要直接与数据库进行交互-从仅读取数据的其他系统(Crystal Reports)到插入数据的系统(不一定使用我设计的API;它可能是由笨拙的经理,他刚刚发现了VBScript并具有SQL框的SA密码。如果数据库不尽如人意,那么-再见数据库。
如果您的数据很重要,那么可以,使用外键,创建一套存储过程来与数据进行交互,并制作出最坚固的数据库。如果您的数据不重要,那么为什么要建立一个数据库呢?
更新:我现在总是使用外键。对于“他们的复杂测试”的异议,我的回答是“编写单元测试,这样他们根本不需要数据库。使用该数据库的任何测试都应正确使用它,并包括外键。如果设置很麻烦,找到一种更轻松的方法来进行设置。”
假设您正在使用外键。您正在编写一个自动测试,内容为“当我更新财务帐户时,它应该保存交易记录”。在此测试中,您只关心两个表:accounts
和transactions
。
但是,accounts
具有的外键contracts
,contracts
具有的fk clients
,clients
具有的fk cities
,cities
具有的fk states
。
现在,如果没有在与测试无关的四个表中设置数据,数据库将不允许您运行测试。
对此至少有两种可能的观点:
在运行测试时,也可能暂时关闭外键检查。至少MySQL 支持此功能。
“它们会使删除记录变得更加麻烦-如果其他表中有外键违反该约束的记录,则无法删除“主”记录。”
重要的是要记住,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 您正在使用的服务器。
没有充分的理由不使用它们……除非我认为孤立行对您来说没什么大不了的。
更大的问题是:你会蒙着眼睛开车吗?如果您开发的系统没有参照约束,便会这样。请记住,业务需求发生了变化,应用程序设计发生了变化,代码中相应的逻辑假设发生了变化,逻辑本身可以重构,等等。通常,数据库中的约束是在当代逻辑假设下放置的,似乎对于特定的一组逻辑断言和假设是正确的。
在应用程序的整个生命周期中,引用检查和数据检查会限制通过应用程序收集数据的工作,尤其是在新需求推动逻辑应用程序更改时。
对于此清单的主题 -从实时交易处理系统的角度来看,外键本身并不“改善性能”,也不显着“降低性能”。但是,在HIGH批量“批处理”系统中,用于约束检查的总成本较高。因此,这是实时交易与批处理交易的区别;批处理-顺序处理的批处理的总成本(通过约束检查确保的总成本)会对性能造成影响。
在一个设计良好的系统中,将在处理批处理之前“进行”数据一致性检查(尽管如此,这里也会产生成本);因此,在加载期间不需要进行外键约束检查。实际上,应暂时禁用所有约束(包括外键),直到处理批次为止。
查询性能 -如果在外键上联接表,请注意以下事实:外键列未索引(尽管相应的主键已按定义索引)。为此,通过对外键建立索引,对任何键进行索引,并在已索引的表上连接表有助于提高性能,而不是通过对具有外键约束的未索引键进行联接。
更改主题,如果数据库仅支持网站显示/呈现内容/等并记录点击,则出于这种目的,在所有表上都具有完全约束的数据库已被淘汰。想一想。大多数网站甚至都不使用数据库。对于类似的要求,如果只是说要记录数据而不是每个人都引用数据,请使用没有限制的内存数据库。这并不意味着没有数据模型,是逻辑模型,但是没有物理数据模型。
根据我的经验,最好避免在数据库关键应用程序中使用FK。我不会不同意这里的人,他们说FK是一种很好的做法,但是在数据库庞大且每秒CRUD操作数量巨大的情况下,它是不实际的。我可以不命名而共享...最大的投资银行之一在数据库中没有一个FK。这些约束由程序员在创建涉及DB的应用程序时处理。根本原因是,每当执行新的CRUD时,它都必须影响多个表并为每个插入/更新进行验证,尽管对于影响单行的查询而言,这并不是一个大问题,但当您处理时,它确实会造成巨大的延迟大型银行必须将其作为日常任务来进行的批处理。
最好避免使用FK,但其风险必须由程序员处理。
“在添加记录之前,检查另一个表中是否存在对应的记录”是业务逻辑。
以下是一些您不希望在数据库中使用的原因:
如果业务规则发生更改,则必须更改数据库。在很多情况下,数据库将需要重新创建索引,而在大型表上这很慢。(更改规则包括:允许访客发布消息或允许用户删除已发表评论的帐户,等等)。
更改数据库并不像通过将更改推送到生产存储库来部署软件修订那样容易。我们希望避免尽可能地更改数据库结构。数据库中存在的业务逻辑越多,您就越需要更改数据库(并触发重新索引)。
TDD。在单元测试中,您可以用数据库代替模拟并测试功能。如果您的数据库中有任何业务逻辑,则您未在进行完整的测试,因此需要使用数据库进行测试或出于测试目的而在代码中复制业务逻辑,从而复制逻辑并增加了逻辑无法在数据库中工作的可能性。同样的方法。
在不同的数据源中重用您的逻辑。如果数据库中没有逻辑,我的应用程序可以从数据库中的记录创建对象,从Web服务,json文件或任何其他来源创建对象。我只需要换出数据映射器实现,就可以将我的所有业务逻辑与任何源一起使用。如果数据库中存在逻辑,那么这是不可能的,您必须在数据映射器层或业务逻辑中实现该逻辑。无论哪种方式,您都需要在代码中进行那些检查。如果数据库中没有逻辑,我可以使用不同的数据库或平面文件实现将应用程序部署在不同的位置。
我同意前面的答案,因为它们对于保持数据一致性很有用。但是,几周前Jeff Atwood发表了一篇有趣的文章,讨论了标准化和一致数据的利弊。
简而言之,在处理大量数据时,非规范化数据库会更快。并且您可能不关心依赖于应用程序的精确一致性,但是它会迫使您在处理数据时要更加小心,因为数据库不会。
Clarify数据库是没有主键或外键的商业数据库的示例。
http://www.geekinterview.com/question_details/18869
有趣的是,技术文档不遗余力地解释了表如何关联,使用哪些列来联接它们等。
换句话说,他们可以将表与显式声明(DRI)联接在一起,但是他们选择不这样做。
因此,Clarify数据库充满了不一致之处,并且表现不佳。
但是我想这使开发人员的工作更加轻松,而不必编写代码来处理参照完整性,例如在删除,添加之前检查相关行。
我认为,这是关系数据库中没有外键约束的主要好处。至少从恶魔般的角度来看,它使开发更容易。
我只知道Oracle数据库,没有其他数据库,而且我可以说外键对于维护数据完整性至关重要。在插入数据之前,需要制作数据结构并使其正确。完成此操作后-这样就创建了所有主键和外键-工作就完成了!
含义:孤行?不。我一生中从未见过。除非一个糟糕的程序员忘记了外键,否则他是否要在另一个级别上实现它。在Oracle中,两者都是巨大的错误,这将导致数据重复,孤立数据,从而导致数据损坏。我无法想象没有实施FK的数据库。在我看来,这很混乱。有点像Unix许可系统:想象每个人都是root。想想混乱。
就像主键一样,外键是必不可少的。就像在说:如果我们删除主键怎么办?好吧,总会发生混乱。就是那样 您不能将主键或外键职责移到编程级别,它必须在数据级别。
缺点 ?是的,一点没错 !因为在插入时,将要进行更多检查。但是,如果数据完整性比性能更重要,那就很容易了。Oracle上的性能问题更多地与PK和FK附带的索引有关。
它们会使删除记录变得更加麻烦-如果其他表中有外键违反该约束的记录,则无法删除“主”记录。您可以使用触发器进行级联删除。
如果您不明智地选择了主键,那么更改该值将变得更加复杂。例如,如果我将“客户”表的PK作为该人的名字,并将该键设置为“订单”表中的FK”,那么如果客户要更改其姓名,那就太麻烦了。 。但这只是伪劣的数据库设计。
我相信使用Fireign密钥的优势胜过任何假定的劣势。
验证外键约束需要花费一些CPU时间,因此有些人忽略了外键以获得一些额外的性能。
我听到的争论是前端应该具有这些业务规则。首先,当您不应该允许任何会破坏约束的插入时,外键会“增加不必要的开销”。我同意吗?不,但这是我一直听到的。
编辑:我的猜测是他指的是外键约束,而不是外键概念。
在整个应用程序生命周期中的可维护性和稳定性如何?大多数数据的寿命比使用该数据的应用程序更长。关系和数据完整性非常重要,以至于无法期望下一个开发团队在应用程序代码中正确实现它。如果您尚未使用不尊重自然关系的脏数据来处理数据库,那么您会这样做。数据完整性的重要性将变得非常明显。
我还认为外键在大多数数据库中都是必需的。唯一的缺点(除了具有强制一致性所带来的性能损失外)是拥有外键使人们可以编写假定存在功能性外键的代码。那绝对是不允许的。
例如,我已经看到人们编写代码,将代码插入到被引用的表中,然后在不验证第一个插入成功的情况下尝试将其插入到引用表中。如果以后删除外键,则会导致数据库不一致。
您也不能选择对更新或删除采取特定行为。无论是否存在外键,您仍然需要编写代码来执行所需的操作。如果您假定删除不是级联的,则删除将失败。如果您假设对引用列的更新没有传播到引用行,则更新将失败。为了编写代码,您可能不具备这些功能。
如果启用了这些功能,那么您的代码无论如何都会模拟它们,并且您将失去一些性能。
因此,摘要...。如果您需要一致的数据库,则外键至关重要。切勿假定外键在您编写的代码中存在或起作用。
我赞同德米特里的答案-很好。
对于那些担心FK经常带来的性能开销的人,有一种方法(在Oracle中)可以让您获得FK约束的查询优化器优势,而无需在插入,删除或更新期间进行约束验证的开销。那就是用属性RELY DISABLE NOVALIDATE创建FK约束。这意味着查询优化程序ASSUMES,在构建查询时已实施了约束,而数据库并未实际实施约束。在填充带有FK约束的表时,您必须非常小心地承担起责任,以确保您的FK列中没有违反约束的数据,就像您这样做一样。可能从涉及此FK约束所在的表的查询中获得不可靠的结果。
我通常在数据集市模式中的某些表上使用此策略,但在集成的暂存模式中不使用此策略。我确保要复制数据的表已经强制执行了相同的约束,或者ETL例程强制执行了约束。
许多在这里回答问题的人都过于依赖通过引用约束实现的引用完整性的重要性。在具有参照完整性的大型数据库上工作效果不佳。Oracle似乎特别擅长级联删除。我的经验法则是应用程序永远不要直接更新数据库,而应该通过存储过程。这将代码库保留在数据库中,并意味着数据库保持其完整性。
在许多应用程序可能正在访问数据库的地方,由于引用完整性约束的确会出现问题,但这取决于控件。
还有一个更广泛的问题是,应用程序开发人员可能有非常不同的要求,数据库开发人员可能不一定那么熟悉。
如果您绝对确定一个基础数据库系统将来不会更改,那么我将使用外键来确保数据完整性。
但是,这是另一个非常现实的理由,根本不使用外键:
您正在开发一种产品,该产品应支持不同的数据库系统。
如果您正在使用能够连接到许多不同数据库系统的实体框架,则可能还需要支持“免费的开源”无服务器数据库。并非所有这些数据库都支持您的外键规则(更新,删除行...)。
这可能导致不同的问题:
1.)创建或更新数据库结构时,您可能会遇到错误。也许只会出现静默错误,因为数据库系统只会忽略您的外键。
2.)如果您依赖于外键,则可能会在业务逻辑中进行更少甚至没有数据完整性检查。现在,如果新数据库系统不支持这些外键规则或仅以其他方式运行,则必须重写业务逻辑。
您可能会问:谁需要不同的数据库系统?好吧,不是每个人都能负担得起或想要一台功能完善的SQL Server。这是软件,需要维护。其他人已经在其他数据库系统上投入了时间和金钱。无服务器数据库非常适合仅在一台计算机上的小型客户。
没有人知道所有这些数据库系统的行为方式,但是经过完整性检查的业务逻辑始终保持不变。
FK可能会导致您出现问题的一种情况是,即使您不再希望该密钥可用,您仍具有(在查找表中)引用该密钥的历史数据。
显然,解决方案是预先设计更好的东西,但是我正在考虑现实世界中的情况,在这种情况下,您并不总是拥有完整解决方案的控制权。
例如:也许您有一个查找表customer_type
,其中列出了不同类型的客户-假设您需要删除某种客户类型,但是(由于业务限制)无法更新客户端软件,并且没有人设想这种情况在开发软件时,即使您知道引用该行的历史数据无关紧要,但实际上它是其他表中的外键可能会阻止您删除该行。
在被烧毁了几次之后,您可能会远离数据库关系的实施。
(我并不是说这很好,只是给出一个原因,您可以决定一般避免使用FK和db约束。)