【问题标题】:How can Hard Deletes work when Foreign Keys are involved当涉及外键时,硬删除如何工作
【发布时间】:2017-03-03 02:02:15
【问题描述】:

我需要一些智慧来解决以关系数据库为中心的应用程序中关于软删除与硬删除的争论。像 thisthisthis 这样的评论都劝阻软删除,因为它基本上是一种 hack、肮脏、简单的出路,并声称硬删除是更好的方法。

我很乐意同意。但似乎没有人提到如何处理一个主要的、不可避免的问题:当外键约束阻止删除一行时,如何硬删除一行,而级联删除不是一种选择?

本质上,如果您想硬删除实体 A,而实体 B 指向它,并且出于业务原因根本无法删除 B,那强制 em> 你要使用某种形式的软删除(即将记录保留在原处)?

如果您的客户“Acme”进行了交易 10001,则不能简单地硬删除 Acme,因为几乎所有企业都需要这样做:

  1. 没有交易可以删除
  2. 交易必须指向进行交易的客户

例如,无论 Acme 如何“删除”(硬、软、超中)销售报告,仍然必须显示交易 #10001 是由客户 Acme 于 2010 年 5 月 1 日进行的——即使如果 Acme 不再存在。因此,它要求 Acme 至少仍存储在系统中的某处

硬删除的拥护者不断提出一个话题:使用适当的审计表,并在其中保留已删除对象的序列化副本。但是我们不能将事务#10001 的 FK 重定向到审计表中的这一特定行。然后怎样呢?创建一个反映“客户”模式的“OldCustomers”表,并以某种方式重定向系统中指向 Acme 的每个 FK?我不是数据库专家,所以我不知道这是否可能,但即使是这样,现在您的所有报告查询都必须考虑两个表(听起来比必须将AND IsDeleted = false 附加到所有查询更糟糕)

简而言之,我觉得我错过了一些东西,因为硬删除和软删除是有争议的——这意味着它们都是可行的选择。但据我了解,在 95% 的业务案例中,不可能使用硬删除(在技术层面上,由于 FK 限制,在逻辑层面上,仅仅是因为一件事不能在另一件事时消失指向它)

【问题讨论】:

  • 你基本上是在征求意见。没有正确/错误的方式。只有满足您的业务需求。任何说“使用 X,因为它是唯一正确的方法”的人都充满了温暖的公牛叶子”
  • @MarcB 不,我不是在征求意见,而是在问它是如何可能的(无论是在技术意义上还是逻辑意义上)硬删除可以在那里工作是否涉及约束。
  • Re“如果你想硬删除实体 A,而实体 B 指向它,并且出于业务原因 B 根本无法删除”:你不是说,A 根本不能被删除?
  • @philipxy:不,我的意思是 B 不能通过一些级联删除来删除。 A(客户)将被删除,但 B(该客户的交易)由于明显的商业原因不能被删除。如果客户记录只是消失了,即使我们在 Transactions 表中有一个可为空的 FK 列,该特定事务的 FK 值应该只具有 NULL 是否可以接受?当您查询该行时,它基本上表示“没有与此交易关联的客户”?从商业角度来看,这也是不可接受的

标签: database-design entity enterprise referential-integrity


【解决方案1】:

TL;DR 适当的表格和约束是适当建模的一部分。您的问题错误地假设了表格和约束的外观限制。


来自您的评论:

是否可以接受该特定事务的 FK 值应为 NULL?当您查询该行时,它基本上表示“没有与此交易关联的客户”

嗯, 没有 当前 客户与此交易相关联。如果您希望记录与此交易相关的过去客户,那么您必须记录它。有关客户或交易的任何其他数据也是如此。硬删除建议假设您保留所需的历史数据。不要将实现这一目标的想法限制在仅将 FK 归零、级联、将标志/日期列添加到现有表或其他任何事情上。正确建模现在和过去,包括在每个选择的应用程序情况发生变化时需要作为 DBMS 事务发生的数据库更改。软删除的建议只是将某些当前和历史数据放入同一个表而不是不同的表中。这仅适用于当前和历史情况的非常简单模型。

仅针对当前应用情况设计数据库通常很简单。但如果我们确实关心过去,我们通常只关心其中的部分。如果是这样,在某些应用程序情况从当前变为过去时,我们可以将相关当前状态的快照复制到历史状态。使用软删除标志与日期标记数据是未注明日期与已注明日期的历史数据的组合表版本,我们只关心当前与过去的情况,我们只关心 那个何时 em> 发生了变化。

“时间”数据库或多或少地记录了当前的情况和一堆过时的曾经的当前情况。这种使用当前数据结构的过去数据记录简化了对当前和过去数据的理解和查询。 (查询时态数据库可以促进的时间间隔可能会变得相当复杂。)但事实证明,制作给定当前数据设计的时态版本不仅仅涉及将日期列添加到现存的当前数据表。它需要对当前数据进行重构,将其分解为具有更多约束的较小表。这是因为不同类型的应用情况变化需要对现有当前数据设计的不同列组合进行约会。 (硬历史快照设计和软历史快照设计必须解决这个问题,但仅限于有限的过去/历史。)

“无软删除”背后的理念是,拥有两个不同的上下文更安全,每个上下文都限制了用户和实施者可能犯的错误。 “历史”与“时间”背后的想法是,它可以更简单地对过去应用情况的有限预定范围进行建模和查询。然而,当您应用合理的设计原则对您决定想要的有关应用程序当前和过去情况的任何数量的数据进行建模时,每种方法选项的设计都会自然而然地出现。

PS 如果您想了解时态数据库,请阅读 Lorentzos、Date 和 Darwen,避免使用 Snodgrass。

【讨论】:

  • 首先,我同意你的观点,理想情况下,我希望交易以某种方式表明 Acme 客户,而不是 ,如果它被硬删除。但是,您是说如果不涉足时态数据库领域,这是不可能的吗?它看起来像是一种相对较新的技术(刚刚在 v2016 中引入 SQL Server),我希望有一种经过时间考验的方法。此外,复杂性似乎有点过头了。在这个假设的用例中,我不需要在每次更改 Acme 的配置文件时插入新记录,而是一个非常简单的要么在系统中要么不活跃
  • 回复“但你是说”?不,我不止一次说过,你需要设计你想要的当前和过去的数据,包括确定如何根据变化适当地将数据从当前移动到过去。我还说时间通常是矫枉过正的。我还说过,只有 非常简单 的案例可以摆脱一些“活跃与否”的情况。我会尽可能添加一个客户交易示例,但您可以立即尝试。对您想要的当前和过去数据进行设计。然后你会看到你的(可能)非常简单的软包的硬版本是什么样子的。
  • 我们可能是误会了。 过去当前客户记录的设计没有任何不同的理由。例如,如果客户是过去的客户,他们仍然可以在地址列中保留相同的数据。
  • 您关于“不可能使用硬删除”的论点是基于错误的概念。模拟当前和历史状态您关心的;然后告诉 DBMS 该设计中的某些状态不能通过声明适当的 FK 来产生。试试吧。见 TL;DR。祝你好运。
  • 我没有理解您要描述的内容。你能指出任何展示这个概念的文章或页面吗?
【解决方案2】:

我使用大量使用软删除的时间设计。或者现在通过执行相同的查询“as of”来获取当前数据。该设计允许硬删除(很少使用)、软删除(最常用)和硬删除(如软删除但不允许撤消)。这种设计允许人们“从”日期执行查询,并返回与在该日期执行查询时返回的结果相同的结果。

不用说,如果“旧”数据和“当前”数据包含在不同的表中,这将非常困难。

查看您的参考资料,我注意到对性能的担忧很大,但没有实际的性能下降示例,第一个参考资料甚至承认“[i]在实践中,过滤非活动行不会花费太多本身。”

这与我对“版本化”表的测试相匹配,其中包含数百万个版本的数以百计的实体。

我通过提供仅公开当前数据的视图和其他公开整个历史记录的视图(以及其他需要的视图)来解决复杂性问题。这对我来说没问题,因为我倾向于让应用访问视图而不是表格。

一般的抱怨是,当您深入了解它时,使用软删除会使数据库开发人员的工作变得更加困难。这是真实的。但我们的工作是让用户的工作更轻松——不是我们自己的。

我女儿是一家大型银行的财务分析师。她经常告诉我,如果她能及时回顾过去某个特定日期的数据,她的工作会容易得多。当然有档案。但通常这只显示数据归档时的最终状态。随着账户的开立、变更并最终关闭,她无法完成整个进程。 (这些是大型企业退休账户,而不是定期支票或储蓄账户。)

但她无法让她的 IT 部门感兴趣,因为这“工作量太大”。伤心。

【讨论】:

    【解决方案3】:

    我认为让硬删除工作并同时保留某种记录的一种方法是不将 FK 用于关系,而只是使用类似于 FK 但不保证引用完整性的标识符字段。然后,您只需要处理不再使用该 id 找到用户的情况。

    【讨论】:

    • 是的,我似乎倾向于你的方法。这似乎是现在事情的发展方式,NoSQL 文档数据库中没有 FK 之类的东西(如果我没记错的话)
    猜你喜欢
    • 2018-05-18
    • 1970-01-01
    • 2010-09-18
    • 2012-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多