如何在数据库中处理删除?


44

我想在Web应用程序中实现“取消删除”功能,以便用户可以改变主意并恢复已删除的记录。关于如何实施的想法?我考虑过的一些选项实际上是删除有问题的记录,并将更改存储在单独的审核表中,或者不删除记录,而是使用布尔“已删除”列将其标记为已删除。后一种解决方案在正常情况下将需要附加的应用程序逻辑来忽略“已删除”的记录,但将使在应用程序端恢复记录变得容易得多。


我忘了提到在第二种情况下,在经过一段合理的时间后,需要删除或移动标记的记录。
Abie

您正在使用什么数据库?
埃文·卡罗尔

临时表是SQL Server 2016及更高版本的最佳解决方案。
Sameer

Answers:


37

是的,我肯定会选择第二个选项,但是我会在日期字段中再添加一个字段。

因此,您添加:

delete       boolean
delete_date  timestamp

这将使您有时间进行不删除操作。

如果时间少于一个小时,则可以取消删除。

要真正删除已删除的条目,只需创建一个存储过程即可,将delete设置为true且时间大于一小时,以清除每个条目并将其作为cron选项卡运行,每24小时运行一次

小时只是一个例子。


或者,您可以使用另一个标志- cleaned或某物-指示与该记录关联的数据已正确,全面地删除。除非cleaned为真,否则该记录可以不删除,在这种情况下,该记录将无法恢复。
Gaurav

14
这是常见的方法。我通常使用一个deleted_at同时包含delete布尔值和delete_date时间戳记的字段。如果deleted_at为is ,则NULLcase deleteFALSEand delete_dateNULLdeleted_at包含时间戳记;为case deleteTRUEdelete_date包含时间戳,为您节省时间,存储和应用程序逻辑。
Julien

1
我喜欢布尔值和日期字段。根据实现删除逻辑的方式,您甚至可以拥有一个单独的表,其中包含日期和“已删除”记录的唯一键。存储过程使此过程变得容易。与8+相比,它占用每行所需的额外空间低至1位。您也可以每天报告删除操作,而无需触摸源表。
AndrewSQL 2011年

注意:delete是MySQL中的保留字。
詹森·里卡德

请记住,deleted查询未删除的行时,对字段进行过滤后的索引可以大大提高性能
Ross Presser,

21

在我们的应用程序中,无论如何,我们并没有真正按照用户的要求删除任何内容(我们的客户处于受监管的环境中,在其中删除任何内容都可能导致法律问题)。

我们将较旧的版本保存在单独的审核表中(因此对于表some_table,其中的表也称为some_table_audit)是相同的,除了具有附加的版本标识符(如果DB支持足够细的时间值,则为时间戳,整数版本号)或作为通用审计表外键的UUID等),并通过触发器自动更新审计表(因此我们不需要使所有更新记录的代码都知道审计要求)。

这条路:

  • 删除操作只是简单的删除-无需在其中添加任何额外的代码(尽管您可能希望记录谁要求删除哪些行,即使它们实际上并未删除)
  • 插入和更新同样简单
  • 您可以通过仅将“正常”行返回到旧版本来实施取消删除或还原操作(审核触发器将再次触发,因此审核跟踪表也将反映此更改)
  • 您可以提供机会查看或还原到任何以前的版本,而不仅仅是取消删除最后一个
  • 您不必添加“标记为已删除?” 检查指向该表的每个代码点,或者对删除/更新行的每个代码点进行“更新审计副本”逻辑(尽管您需要决定如何处理审计表中的已删除行:我们确实有一个删除/未标记每个版本,因此如果删除记录并稍后将其删除则历史记录中没有漏洞)
  • 将审计副本保存在单独的表中意味着您可以轻松地将它们划分为不同的文件组。

如果使用时间戳而不是(或同时使用)整数版本号,则可以根据需要在指定的时间后使用它来删除较旧的副本。但是这些天磁盘空间相对便宜,因此除非我们没有理由丢弃旧数据(即数据保护法规说您应该在X个月/年之后删除客户端数据),否则我们不会这样做。


此答案已有大约几年的时间,此后已经改变了一些可能影响此类计划的关键因素。我不会详细介绍,只是为了今天阅读此书的人们的利益:

  • SQL Server 2016引入了“系统版本化的时态表”,该表为您完成了许多工作,此外还提供了一些不错的语法糖,以使历史查询更易于构建和维护,并且它们协调了之间的架构更改子集。基本表和历史记录表。他们并非没有警告,但它们是实现此目的的有力工具。在其他数据库系统中也可以使用类似的功能。

  • 数据保护法规的变化,尤其是GDPR的引入,可以显着改变何时应该硬删除数据的问题。在考虑时,您必须权衡不删除可能对审核目的有用(或确实是法律上要求)的数据与在考虑考虑时需要尊重人民权利(既包括一般性法律又明确规定相关法律)的平衡您的设计。这可能是系统版本化的时态表的问题,因为您无法修改历史记录以清除个人数据,而无需进行模式短期更改即可在进行更改时关闭历史记录跟踪。


您如何处理列的删除和重命名?是否将所有内容都设置为可空?
Stijn

1
@Stijn:通常不更改结构,以免出现太多变化。Colunms通常在生产中已经存在后就永远不会删除-如果停止使用它们,则删除任何会阻止它们Bennig NULL的约束(或添加默认值以使用“魔术值”来处理约束,尽管感觉更肮脏)并停止在其他代码中引用它们。对于重命名:添加新名称,停止使用旧名称,并根据需要将数据从旧版本复制到新版本。如果要重命名列,只需确保同时对基本表和审计表进行了相同的更改。
David Spillett

9

使用布尔删除列,如果表开始增长并变得很大,您将遇到问题。我建议您每周一次将已删除的列(或多或少,取决于您的规范)移动到另一张表中。这样,您将拥有一个不错的小型活动表和一个大型表,其中包含一段时间内收集的所有记录。


7

我会去单独的桌子。Ruby on Rails有一个acts_as_versioned插件,该插件基本上可以_version在更新后将带有后缀的行保存到另一个表中。尽管您不需要那种确切的行为,但它也应该适合您的情况(删除之前先进行复制)。

像@Spredzy一样,我也建议添加一delete_date列,以便能够以编程方式清除X小时/天/天之后未恢复的记录。


4

我们内部在此问题上使用的解决方案是拥有一个状态列,其中包含一些针对对象的某些特定状态的硬编码值:删除,活动,非活动,打开,关闭,已阻止-每个状态在应用程序中使用了某些含义。从数据库的角度来看,我们不删除对象,我们只是更改状态并保留对象表中每次更改的历史记录。


3

当您说“后一种解决方案将需要其他应用程序逻辑来忽略'已删除'的记录”时,简单的解决方案是拥有一个将它们过滤掉的视图。


这不仅仅是一个观点问题。在集合上执行的任何操作都必须排除“已删除”记录。
艾比(Abie)

2

与Spredzy建议的类似,我们在所有应用程序中都使用时间戳字段进行删除。布尔值是多余的,因为设置了时间戳记表明该记录已被删除。这样,我们的PDO总是添加AND (deleted IS NULL OR deleted = 0)到select语句中,除非模型明确要求包括已删除的记录。

目前,除了包含Blob或文本的表格外,我们不会对任何其他表格进行垃圾回收;如果对记录进行了很好的规范化,那么空间就deleted很小了,对字段进行索引对选择速度的影响有限。


0

您也可以将责任放在用户(和开发人员)上,并依次显示“您确定吗?”,“您确实确定吗?” 和“您是绝对,肯定和真正确定的吗?” 删除记录之前的问题。轻度玩笑,但值得考虑。


0

我过去经常在表行中看到像“ DeletedDate”这样的列,但我不喜欢它们。“删除”的概念是,不应首先输入该条目。实际上,它们无法从数据库中删除,但我不希望它们与我的热数据一起使用。根据定义,逻辑上删除的行是冷数据,除非有人特别希望看到删除的数据。

此外,编写的每个查询都必须专门排除它们,索引也需要考虑它们。

我希望看到的是数据库体系结构级别和应用程序级别的更改:创建一个称为“已删除”的架构。每个用户定义的表在“已删除”模式中具有相同的等效项,带有一个额外的字段来保存元数据-删除该表的用户以及删除该表的时间。需要创建外键。

接下来,删除变为插入删除。首先,将要删除的行插入其“已删除”架构对应项中。然后可以删除主表中的相关行。但是,确实需要在该行的某个地方添加额外的逻辑。可以处理违反外键的行为。

必须正确处理外键。不好的做法是在逻辑上删除行,但其主/唯一行在引用该表的其他表中具有列。无论如何这都不应该发生。常规作业可以删除寡妇行(尽管存在外键,但其主键在其他表中没有引用的行。但是,这是业务逻辑)。

总体好处是减少了表中的元数据并带来了性能提升。“ deletedDate”列表示该行实际上不应该在此处,但是为了方便起见,我们将其保留在此处并让SQL查询对其进行处理。如果已删除行的副本保留在“已删除”模式中,则包含热数据的主表将具有较高百分比的热数据(假设它已及时存档),并且不必要的元数据列会更少。索引和查询不再需要考虑此字段。行大小越短,页面上可以容纳的行数越多,SQL Server可以运行得越快。

主要缺点是操作的大小。现在有两种操作,而不是一种,还有额外的逻辑和错误处理。与更新否则将导致更新的单列相比,它可能导致更多的锁定。事务在表上的锁定时间更长,并且涉及两个表。至少以我的经验,删除生产数据很少完成。即便如此,在主表之一中,近1亿个条目中有7.5%的“ DeletedDate”列中有一个条目。

作为对该问题的解答,应用程序必须知道“取消删除”。只需按照相反的顺序进行操作:将“已删除”模式中的行插入主表,然后从“已删除模式”中删除行。再次需要一些额外的逻辑和错误处理,以确保避免错误,外键问题等。

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.