数据库中的删除应该如何处理?

Abi*_*bie 45 database-design audit delete

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

Spr*_*dzy 38

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

所以你添加:

delete       boolean
delete_date  timestamp
Run Code Online (Sandbox Code Playgroud)

它会让您有时间进行取消删除操作。

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

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

小时只是一个例子。

  • 这是常见的做法。我通常使用一个字段“deleted_at”来保存“delete”布尔值和“delete_date”时间戳的语义。如果`deleted_at`是`NULL`处理case`delete`是`FALSE`并且`delete_date`是`NULL`,`deleted_at`包含一个时间戳句柄`delete`是`TRUE`并且`delete_date`包含一个时间戳,为您节省时间、存储和应用程序逻辑。 (14认同)

Dav*_*ett 22

在我们的应用程序中,我们实际上并没有根据用户的请求删除任何内容(我们的客户处于受监管的环境中,删除任何内容都可能导致法律问题)。

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

这边走:

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

如果使用时间戳而不是(或以及)整数版本号,您可以使用它在一段时间后根据需要删除旧副本。但是现在磁盘空间相对便宜,所以除非我们有理由删除旧数据(即数据保护规定,您应该在 X 个月/年后删除客户端数据),否则我们不会删除。


这个答案已经存在了几年,从那时起,可能影响这种规划的一些关键因素发生了变化。我不会详细介绍,但为了今天阅读本文的人的利益,我会简要介绍一下:

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

  • 数据保护立法的变化,特别是 GDPR 的引入,可以显着改变何时应该硬删除数据的问题。您必须权衡不删除可能对日后审计目的有用(或实际上是法律要求)的数据与需要尊重人民权利(一般而言和相关立法中具体规定的权利)之间的平衡。你的设计。这可能是系统版本化时态表的一个问题,因为您无法修改历史以清除个人数据,而无需架构短期更改以在进行更改时关闭历史跟踪。


poe*_*nca 9

使用布尔删除列,如果您的表开始增长并变得非常大,您将开始遇到问题。我建议您每周将删除的列(或多或少取决于您的规格)移动到不同的表中。这样你就有了一个漂亮的小活动表和一个包含随着时间推移收集的所有记录的大表。


小智 7

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

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