外键怎么了?
我记得在播客014中听到Joel Spolsky提到他几乎没有使用过外键(如果我没记错的话)。但是,对我来说,它们对于避免整个数据库中的重复和后续数据完整性问题至关重要。
人们为什么会有一些扎实的理由(避免按照堆栈溢出原则进行讨论)?
编辑:"我还没有创建外键的理由,所以这可能是我真正设置外键的第一个理由。"
解决方案
回答
没有充分的理由不使用它们……除非我认为孤立行对我们来说没什么大不了的。
回答
外键对于任何关系数据库模型都是必不可少的。
回答
我一直认为不使用它们是懒惰的。有人告诉我应该总是这样做。但是后来,我没有听乔尔的讨论。我不知道他可能有很好的理由。
回答
它们会使删除记录变得更加麻烦,而在其他表中有外键违反该约束的记录的情况下,则无法删除"主"记录。我们可以使用触发器进行级联删除。
如果我们不明智地选择了主键,那么更改该值将变得更加复杂。例如,如果我将"客户"表的PK作为该人的名字,并将该键设置为"订单"表中的FK",那么如果客户要更改其名称,那就太麻烦了。但是,这只是伪劣的数据库设计。
我相信使用Fireign Key的优势胜过任何假定的劣势。
回答
我听到的争论是前端应该具有这些业务规则。当我们首先不应该允许任何会破坏约束的插入时,外键会"增加不必要的开销"。我同意吗?不,但这是我一直听到的。
编辑:我的猜测是他指的是外键约束,而不是外键概念。
回答
我可以看到一些使用外键的原因(就像有人提到的那样,孤立的行很烦人),但我也从未使用过。对于相对健全的数据库架构,我认为不是100%需要它们。约束是好的,但是我认为通过软件强制约束是更好的方法。
亚历克斯
回答
我同意前面的答案,因为它们对于保持数据一致性很有用。但是,几周前Jeff Atwood发表了一篇有趣的文章,讨论了标准化和一致的数据的利弊。
简而言之,非规范化数据库在处理大量数据时会更快。并且我们可能不关心依赖于应用程序的精确一致性,但是它会迫使我们在处理数据时要更加小心,因为数据库不会。
回答
不使用它们有一个很好的理由:如果我们不了解它们的作用或者使用方法。
在错误的情况下,外键约束可能导致意外事故的瀑布式复制。如果有人删除了错误的记录,则将其撤消将成为一项艰巨的任务。
同样,相反地,当我们需要删除某些东西时,如果设计不当,约束可能会导致各种阻止锁。
回答
验证外键约束需要花费一些CPU时间,因此有些人忽略了外键以获得一些额外的性能。
回答
Clarify数据库是没有主键或者外键的商业数据库的示例。
http://www.geekinterview.com/question_details/18869
有趣的是,技术文档不遗余力地解释了表如何关联,使用哪些列来联接它们等。
换句话说,他们可以将表与显式声明(DRI)联接在一起,但他们选择不这样做。
因此,Clarify数据库充满了不一致之处,并且表现不佳。
但是我想这使开发人员的工作更加轻松,而不必编写代码来处理参照完整性,例如在删除,添加之前检查相关行。
我认为,这是在关系数据库中没有外键约束的主要好处。至少从恶魔般的角度来看,它使开发变得更容易。
回答
@强调这正是导致维护噩梦的心态。
为什么?为什么我们会忽略声明性参照完整性(可以确保数据至少是一致的),而采用所谓的"软件强制执行",这充其量是一个弱小的预防措施。
回答
引用乔·塞尔科(Joe Celko)的话,
"like a size 26 thong, just because you can does not mean you should!"
我敢肯定,有很多应用程序可以解决这个问题,但这并不是最好的主意。我们不能总是依靠应用程序来正确地管理数据库,并且坦率地说,管理数据库对应用程序不是很重要。
如果我们正在使用关系数据库,那么似乎应该在其中定义一些关系。不幸的是,许多应用程序开发人员似乎都接受了这种态度(我们不需要外键),他们不愿为诸如数据完整性之类的愚蠢的事情所困扰(但因为他们的公司没有专门的数据库开发人员而需要这样做)。通常在由这些类型组成的数据库中,只有主键才很幸运;)
回答
我也从那些忘记在外键上放置索引,然后抱怨某些操作很慢的人(因为约束检查可以利用任何索引)而听到这种说法。综上所述:没有充分的理由不使用外键。所有现代数据库都支持级联删除,因此...
回答
我总是使用它们,但是后来我为金融系统创建了数据库。数据库是应用程序的关键部分。如果财务数据库中的数据不完全准确,那么我们在代码/前端设计中投入多少精力就无关紧要了。我们只是在浪费时间。
还有一个事实是,多个系统通常需要直接从其他系统读取数据库,而这些系统只是从读取数据(Crystal Reports)的其他系统到插入数据的系统(不一定使用我设计的API;它可能是由愚蠢的人编写的)刚发现VBScript并具有SQL框的SA密码的机灵经理。如果数据库不尽如人意,那么再见数据库。
如果数据很重要,那么可以,使用外键,创建一套存储过程来与数据进行交互,并制作出最坚固的数据库。如果数据不重要,那么为什么要建立一个数据库呢?
回答
使用外键的原因:
- 你不会得到孤行
- 我们可以得到很好的"删除级联"行为,自动清理表
- 了解数据库中表之间的关系有助于优化器计划查询以实现最有效的执行,因为它可以更好地估计连接基数。
- FK可以很大程度地提示哪些统计数据最重要的是在数据库上收集,从而提高性能
- 它们支持各种自动生成的支持-ORM可以自行生成,可视化工具将能够为我们创建漂亮的架构布局,等等。
- 该项目的新手将更快地进入流程,因为否则显式地记录了隐式关系
不使用外键的原因:
- 我们需要在每个CRUD操作上增加数据库的工作量,因为它必须检查FK一致性。如果我们流失很多,这可能是一个很大的代价
- 通过执行关系,FK可以指定必须添加/删除内容的顺序,这可能导致DB拒绝执行我们想要的操作。 (在这种情况下,可以尝试创建一个孤立行,这通常不是一件好事)。当我们进行大批量更新时,这尤其痛苦,并且我们先加载一个表,然后又加载另一个表,而第二个表创建了一致的状态(但是如果第二次加载失败并且第二个表可能会发生这种情况,我们应该这样做吗?数据库现在不一致?)。
- 有时我们事先知道数据将变得肮脏,我们接受了它,并且希望数据库接受它
- 你只是懒惰:-)
我认为(我不确定!)大多数已建立的数据库都提供了一种指定未强制执行的外键的方法,该外键只是一些元数据。由于不执行强制措施会清除所有不使用FK的原因,因此,如果第二部分中的任何原因均适用,我们可能应该走这条路。
回答
FK可能会导致我们出现问题的一种情况是,即使我们不再希望该密钥可用,我们仍具有引用该密钥的历史数据(在查找表中)。
显然,解决方案是预先设计更好的东西,但是我正在考虑现实世界中的情况,在这种情况下,我们并不总是拥有完整解决方案的控制权。
例如:也许我们有一个查询表" customer_type",其中列出了不同类型的客户,可以说我们需要删除某种客户类型,但是(由于业务限制)无法更新客户端软件,并且没有人计划在开发软件时出现这种情况,即使我们知道引用该行的历史数据是无关紧要的,但实际上它是其他表中的外键可能会阻止我们删除该行。
在被烧毁了几次之后,我们可能会倾向于避免执行数据库关系。
(我并不是说这很好,只是说明为什么我们可以决定一般避免使用FK和db约束)
回答
"它们会使删除记录变得更加麻烦,而在其他表中有外键违反该约束的记录的情况下,我们将无法删除"主"记录。"
重要的是要记住,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 Server。
回答
对我来说,如果要遵循ACID标准,拥有外键以确保引用完整性至关重要。
回答
在这里,我要谈第二大部分的意见,外键是确保我们拥有完整数据的必要项。 ON DELETE和ON UPDATE的不同选项将使我们避开人们在此处提到的有关其使用的"跌倒"问题。
我发现在我所有项目的99%中,我将拥有FK来加强数据的完整性,但是,在极少数情况下,我有客户必须保留其旧数据,无论数据有多糟糕……。但是后来我花了很多时间编写代码,无论如何都只能获取有效数据,所以它变得毫无意义。
回答
使用外键的其他原因:
允许更多地重用数据库
不使用外键的其他原因:
我们正在尝试通过减少重复使用来将客户锁定在工具中。
回答
在整个应用程序生命周期中的可维护性和稳定性如何?大多数数据的寿命比使用它的应用程序更长。关系和数据完整性非常重要,以至于无法期望下一个开发团队在应用程序代码中正确实现它。如果我们尚未使用不尊重自然关系的脏数据来处理数据库,那么我们会这样做。数据完整性的重要性将变得非常明显。
回答
我会回应德米特里(Dmitriy)所说的话,但要补充一点。
我在一个批处理记帐系统上工作,该系统需要在30多个表上插入大量行。我们不允许做数据泵(Oracle),所以我们必须做批量插入。这些表上有外键,但是我们已经确保它们没有破坏任何关系。
在插入之前,我们禁用外键约束,以便Oracle不会永远花时间进行插入。成功插入后,我们重新启用约束。
PS:在一个大型数据库中,该数据库具有许多外键和单个记录的子行数据,有时外键可能是错误的,并且我们可能希望禁止级联删除。对于计费系统中的我们来说,如果执行级联删除操作,将花费很长时间并且对数据库负担太大,因此我们仅在主驱动程序(父)表上的字段上将记录标记为不良。
回答
我还认为外键在大多数数据库中都是必需的。唯一的缺点(除了强制一致性带来的性能损失外)是拥有外键使人们可以编写假定存在功能性外键的代码。那绝对是不允许的。
例如,我见过人们编写代码,将代码插入到被引用的表中,然后在不验证第一个插入成功的情况下尝试将其插入到引用表中。如果以后删除外键,则会导致数据库不一致。
我们也不能选择在更新或者删除时采取特定的行为。无论是否存在外键,我们仍然需要编写代码来执行所需的操作。如果我们假定删除不是级联的,则删除将失败。如果我们假设对引用列的更新没有传播到引用行,则更新将失败。为了编写代码,我们可能不具备这些功能。
如果启用了这些功能,那么代码无论如何都会模拟它们,并且我们会失去一些性能。
因此,摘要...。如果我们需要一致的数据库,则外键至关重要。切勿假定外键在我们编写的代码中存在或者起作用。
回答
这是一个成长的问题。如果我们在教育或者职业生涯中的某个地方花了一些时间来喂养和维护数据库(或者与做过这项工作的有才华的人紧密合作),那么实体和关系的基本原则在思考过程中就根深蒂固。在这些基础知识中,包括如何/何时/为什么在数据库中指定键(主键,外键以及备用键)。这是第二天性。
但是,如果我们过去在RDBMS相关工作中没有获得如此全面或者积极的经验,那么我们可能就不会接触到此类信息。或者,过去可能包括沉浸在一个强烈反对数据库的环境中(例如,"我们那些DBA是白痴,我们很少,我们选择了很少的Java / ccode slinger可以节省一天的时间"),在这种情况下,我们可能强烈反对如果我们只想听,一些推特的奥秘bab语告诉我们FK(以及它们可能暗示的约束)确实很重要。
多数人从小就被教导刷牙很重要。没有它,你能渡过难关吗?当然可以,但是在某些地方,牙齿供应比每顿饭后刷牙要少。如果父母有足够的责任涵盖数据库设计以及口腔卫生,那么我们就不会进行对话了。 :-)
回答
我非常赞同德米特里的回答。
对于那些担心FK经常带来的性能开销的人,有一种方法(在Oracle中)可以让我们获得FK约束的查询优化器优势,而无需在插入,删除或者更新过程中进行约束验证的开销。那就是用属性RELY DISABLE NOVALIDATE创建FK约束。这意味着查询优化程序ASSUMES,在构建查询时已实施了约束,而数据库并未实际实施约束。在使用FK约束填充表时,我们必须非常小心在这里承担责任,以确保FK列中没有违反约束的数据,就像我们这样做一样。可能会从涉及此FK约束所在的表的查询中获得不可靠的结果。
我通常在数据集市模式中的某些表上使用此策略,但在集成的暂存模式中不使用此策略。我确保要复制数据的表已经强制执行了相同的约束,或者ETL例程强制执行了约束。
回答
像许多事情一样,这是一个权衡。这是我们要在何处进行工作以验证数据完整性的问题:
(1)使用外键(单点配置表,功能已经实现,测试,证明有效)
(2)将其留给数据库的用户(可能有多个用户/应用程序更新同一张表,这意味着更多潜在的故障点并增加了测试的复杂性)。
数据库执行(2)的效率更高,维护(1)更容易,风险更低。
回答
更大的问题是:我们会蒙着眼睛开车吗?如果我们开发的系统没有参照约束,那就是如此。请记住,业务需求发生了变化,应用程序设计发生了变化,代码中相应的逻辑假设发生了变化,逻辑本身可以重构,等等。通常,数据库中的约束是在当代逻辑假设下放置的,似乎对于特定的一组逻辑断言和假设是正确的。
在应用程序的整个生命周期中,引用检查和数据检查会限制通过应用程序收集数据的工作,特别是在新需求驱动逻辑应用程序更改时。
对于此列表的主题,从实时交易处理系统的角度来看,外键本身并不"改善性能",也不显着"降低性能"。但是,在HIGH批量"批处理"系统中,用于约束检查的总成本较高。因此,这是实时交易与批处理交易的区别;批处理,其中顺序检查的批处理的总成本(由约束检查确保)增加了性能。
在一个设计良好的系统中,将在处理批处理之前"进行"数据一致性检查(不过,这里也存在相关的成本);因此,在加载期间不需要进行外键约束检查。实际上,包括外键在内的所有约束都应暂时禁用,直到处理批次为止。
如果表是在外键上联接的,则具有QUERY PERFORMANCE的性能,请注意外键列未索引(尽管相应的主键已按定义索引)。为此,通过对外键建立索引,对任何键进行索引,并在已索引的表上连接表有助于更好的性能,而不是通过对具有外键约束的未索引键进行联接。
更改主题,如果数据库仅支持网站显示/渲染内容/等并记录点击,那么出于这种目的,在所有表上都具有完全约束的数据库将被淘汰。想一想。大多数网站甚至都没有为此使用数据库。对于类似的要求,如果只是说要记录数据而不是每个人都引用数据,请使用没有约束的内存数据库。这并不意味着没有数据模型,是逻辑模型,但是没有物理数据模型。
回答
数据结构设计的一个好原则是确保表或者对象的每个属性都受到易于理解的约束。这很重要,因为如果我们或者程序可以依靠数据库中的有效数据,则我们不太可能由于错误数据而导致程序缺陷。我们还花费更少的时间来编写代码来处理错误情况,并且更有可能预先编写错误处理代码。
在许多情况下,可以在编译时定义这些约束,在这种情况下,我们可以编写过滤器以确保属性始终在范围内,否则保存属性的尝试将失败。
但是,在许多情况下,这些约束可以在运行时更改。例如,我们可能有一个"汽车"表,该表具有"颜色"作为属性,该属性最初采用"红色","绿色"和"蓝色"的值。在程序执行期间,可以将有效颜色添加到该初始列表中,并且所添加的新"汽车"可以采用最新颜色列表中的任何颜色。此外,我们通常希望此更新的颜色列表能够在程序重新启动后继续存在。
为了回答问题,事实证明,如果我们需要在运行时可以更改的数据约束条件,并且这些更改必须在程序重新启动后继续存在,所以外键是解决该问题的最简单,最简洁的方法。开发成本是添加一个表(例如"颜色","汽车"表的外键约束和索引),而运行时成本是对最新颜色的额外表查找验证数据,通常可以通过建立索引和缓存来减少运行时成本。
如果我们不使用外键满足这些要求,则必须编写软件来管理列表,查找有效条目,将其保存到磁盘,如果列表很大,则可以有效地对数据进行结构化,确保对列表的任何更新都不损坏列表文件,在有多个读取器和/或者写入器的情况下,提供对列表的串行访问,依此类推。即,我们需要实现许多RDBMS功能。