28

在不同的雇主工作后,我注意到其中一些公司的数据库设计“糟糕”的趋势——主要是排除外键约束。这些事务系统没有 FK 一直困扰着我,这会促进参照完整性。

  • 在交易系统中,是否存在省略 FK 有益的情况?

  • 有没有其他人经历过这种情况,如果有,结果如何?

  • 如果他们遇到这种情况并要求他们维护/增强系统,应该怎么做?

4

6 回答 6

46

我想不出任何情况,如果两列具有依赖关系,则它们之间不应设置 FK 约束。删除参照完整性当然可以加快数据库操作,但要为此付出相当高的成本。

我经历这样的系统,通常的结果是数据损坏,即存在不应该存在的记录(反之亦然)。在这些系统中,人们认为他们没问题,因为应用程序会处理它,而不是关心它:

  • 每个应用程序都必须处理它,而不是一个数据库服务器。
  • 只需要一个错误或恶意应用程序就可以为每个人搞砸。
  • 保护自己是数据库的责任这是它最好的功能之一。

至于你应该做什么,我只是提出了可能出错的事情以及使用 FK 将如何防止这种情况(如果需要,通常会通过成本/收益分析“偏向”我的观点)。然后让公司决定——毕竟这是他们的数据库。

于 2012-08-22T02:22:47.820 回答
12

有一种观点认为,编写良好的应用程序不需要参照完整性。如果应用程序做对了,那么就不需要约束。

这种想法类似于不进行防御性编程,因为如果您正确编写代码,就不会出现错误。虽然是真的,但它根本不会发生。不使用适当的约束要求数据损坏。

至于你应该做什么,你应该鼓励公司在每一个机会中增加约束。你不想把它推到惹麻烦或给自己留下坏名声的地步,但只要环境合适,继续推动它。从长远来看,每个人的生活都会更好。

于 2012-08-22T02:33:24.093 回答
5

就个人而言,我对没有明确声明外键的数据库没有任何问题。但是,这取决于数据库的使用方式。

我使用的大多数数据库都是从一个或多个事务系统派生的相对静态的数据。我并不特别关心影响数据库的恶意更新,因此明确定义外键关系并不是特别重要。

我确实拥有的一件事是非常一致的命名。基本上,每个表都有一个名为 ID 的第一列,这正是该列在其他表中的引用方式(或者,有时带有前缀,当两个实体之间存在多个关系时)。我还尝试坚持这样的数据库中的每一列都有一个描述属性的唯一名称(因此“CustomerStartDate”与“ProductStartDate”不同)。

如果我正在处理有更多“锅里的厨师”的数据,那么我希望更明确地了解外键关系。而且,我更愿意承担外键定义的开销。

这种开销出现在许多地方。创建新表时,我可能想使用“create table as”或“select into”,而不用担心约束的细节。运行更新或插入查询时,我可能不希望检查我知道没问题的事情的数据库开销。但是,我必须强调,一致的命名大大增加了我对一切正常的信心。

显然,我的观点不是 DBA,而是从业者。然而,表格之间的无效关系是我——或我团队的其他成员——几乎不需要处理的事情。

于 2012-08-22T02:35:47.897 回答
3

只要数据库有一个单一的入口点,最终哪个“层”维护参照完整性并不重要。使用外键约束的“内置层”似乎是最有意义的,但如果你有一个坚如磐石的服务层负责同样的事情,那么它可以在必要时自由地打破规则。

我个人使用外键约束并设计我的应用程序,这样他们就不必违反规则。具有保证参考完整性的关系数据更易于使用。

获得的性能可能等同于必须在数据库之外保持完整性而损失的性能。

于 2014-10-14T21:24:26.070 回答
2

在 OLTP 数据库中,我能想到的唯一原因是您是否关心性能而不是数据完整性。在将行插入子表时强制执行 FK 需要在父表上进行索引查找,我可以想象可能存在极端情况,即使这种相对快速的索引查找也太多了。例如,某种非常密集的日志记录,您可以忍受不正确的日志条目,并且执行写入的应用程序很简单,不太可能有错误。

话虽如此,如果您可以忍受损坏的数据,那么您可能一开始就可以在没有数据库的情况下生活。

于 2012-08-22T08:14:26.200 回答
0

如果您主要使用存储过程并且每个应用程序都使用这些存储过程,而不是编写自己的查询,那么没有外键的防御性编程就可以工作。然后你可以很容易地控制它,比标准外键更灵活。

我能想到的一种情况是外键约束不容易使用的情况是权限模块,其中权限可以应用到每个用户或每个组,由布尔值确定。所以权限表中的一些记录有一个用户 ID,而其他记录有一个组 ID。如果您仍然需要外键约束,则必须为相同的互斥信息提供两个不同的字段,并允许它们为空。这意味着添加另一个约束,即允许一个为空,但它们不能都为空,并且 3 个字段的组合必须是唯一的,而不是 2 个字段的组合(用户/组 ID 和权限 ID)。另一种方法是包含相同数据的两个单独的表,这意味着分别维护两个表。

但也许在这种情况下,最好将数据分开。如果您需要相同的字段来根据该记录中的其他数据连接到不同的表,则不能使用外部字段约束,最好将约束保留在存储过程和视图中。

于 2018-08-03T17:42:15.277 回答