#1 楼
是的,我肯定会选择第二种选择,但是我会在日期字段中再添加一个字段。所以您添加了: >这将使您有时间进行删除操作。
如果时间少于一小时,则可以删除。
要真正删除已删除的条目,只需创建一个存储该程序将清除delete设置为true且时间大于一小时的每个条目,并将其作为cron选项卡运行,该选项卡每24小时运行一次。
小时仅是示例。
评论
或者,您可以设置另一个标记-清除或其他标记-表示与该记录关联的数据已正确,全面删除。除非清除为true,否则无法删除该记录,在这种情况下,该记录不可恢复。
–加拉夫
2011年1月4日,9:03
这是常见的方法。我通常使用一个字段delete_at,它同时包含delete布尔值和delete_date时间戳的语义。如果delete_at为NULL,则删除为FALSE,delete_date为NULL,包含时间戳的delete_at,删除为TRUE,并且delete_date包含时间戳,为您节省时间,存储和应用程序逻辑。
–朱利安
2011年1月4日13:30
我喜欢布尔值和日期字段。根据实现删除逻辑的方式,您甚至可以拥有一个单独的表,其中包含日期和“已删除”记录的唯一键。存储过程使此过程变得容易。与8+相比,它占用每行所需的额外空间低至1位。您也可以每天报告删除操作,而无需触摸源表。
– AndrewSQL
2011-1-19的1:46
注意:delete是MySQL中的保留字。
–詹森·里卡德(Jason Rikard)
2011年7月13日在16:59
请记住,在查询未删除的行时,删除字段上的筛选索引可以大大提高性能
–罗斯压机
19年8月1日在17:01
#2 楼
在我们的应用程序中,我们无论如何都不会按照用户的要求删除任何内容(我们的客户处于受监管的环境中,在删除任何内容都可能导致法律问题的环境中)。我们将旧版本保留在单独的审核中表(因此对于表some_table,其中的表也是一个称为some_table_audit的表)与表相同,除了具有附加的版本标识符(如果DB支持足够细粒度的时间值,则为时间戳,整数版本号或UUID是作为常规审核表等),并通过触发器自动更新审核表(因此我们不需要使所有更新记录的代码都知道审核要求)。
这种方式:
删除操作只是一个简单的删除-无需在其中添加任何额外的代码(尽管您可能希望记录谁要求删除哪些行,即使它们不是实际上已删除)
插入和更新同样简单
您可以实现unde只需将“正常”行返回到旧版本即可恢复或还原(审核触发器将再次触发,因此审核跟踪表也将反映此更改)
您可以提供机会查看或还原至任何以前的版本不仅仅是取消删除最后一个
,您不必添加“标记为已删除?”检查指向该表的每个代码点,或者对删除/更新行的每个代码点进行“更新审计副本”逻辑(尽管您需要决定如何处理审计表中的已删除行:我们确实有一个已删除/未标记每个版本,因此如果删除记录并随后将其删除则历史记录中没有漏洞。
将审计副本保存在单独的表中意味着您可以轻松地将它们划分为不同的文件组。 br />
如果使用时间戳记而不是(或同时使用)整数版本号,则可以在需要的一段时间后使用它来删除较旧的副本。但是这些天磁盘空间相对便宜,因此除非我们没有理由删除旧数据(例如,数据保护法规规定您应在X个月/年之后删除客户端数据),否则我们不会这样做。
这个答案已经存在了大约几年时间,从那时起,可能影响这种计划的几项关键因素发生了变化。我不会详细介绍,但是为了使今天的读者受益匪浅:
SQL Server 2016引入了“系统版本化的时态表”,它为您以及其他人,此外还提供了一些不错的语法糖,以使历史查询更易于构建和维护,并且它们协调基础表和历史表之间的模式更改子集。他们并非没有警告,但它们是实现此目的的有力工具。其他数据库系统也具有类似的功能。
对数据保护法规的更改(尤其是GDPR的引入)可以显着改变何时应硬删除数据的问题。在考虑时,您必须权衡不删除可能对审核目的有用(或确实是法律上要求)的数据与在考虑考虑时需要尊重人民权利(既包括一般性法律又明确规定相关法律)的平衡您的设计。这可能是系统版本化的时态表的问题,因为您无法在没有进行模式短期更改的情况下修改历史记录以清除个人数据,从而在更改时关闭历史记录跟踪。
评论
您如何处理列的删除和重命名?是否将所有内容都设置为可空?
– Stijn
13年2月12日在15:12
@Stijn:通常不更改结构,以免出现太多变化。 Colunms通常在生产中已经存在后就永远不会删除-如果它们停止使用,只需删除任何会阻止它们Bennig NULL的约束(或添加默认值以使用“魔术值”来处理约束,尽管这会更脏)并停止在其他代码中引用它们。对于重命名:添加新名称,停止使用旧名称,并根据需要将数据从旧版本复制到新版本。如果要重命名列,只需确保同时对基本表和审计表进行了相同的更改。
– David Spillett
13年2月13日在14:15
#3 楼
使用布尔删除列,如果表开始增长并变得很大,您将开始遇到问题。我建议您每周一次将已删除的列(或多或少,取决于您的规范)移动到另一张表。这样,您将拥有一个漂亮的小型活动表和一个大型表,其中包含了随着时间的推移收集的所有记录。#4 楼
我会去单独的桌子。 Ruby on Rails有一个acts_as_versioned
插件,该插件在更新前基本上将行保存到带有后缀_version
的另一个表中。尽管您不需要那种确切的行为,但它也应该适合您的情况(删除之前先进行复制)。 像@Spredzy一样,我还建议添加
delete_date
列,以便能够以编程方式清除X小时/天/之后的任何时间未恢复的记录。#5 楼
我们内部用于此问题的解决方案是拥有一个状态列,其中包含一些针对对象某些特定状态的硬编码值:删除,活动,非活动,打开,关闭,已阻止-每个状态在应用程序中使用了某些含义。从数据库的角度来看,我们不删除对象,我们只是更改状态并保留对象表中每次更改的历史记录。#6 楼
当您说“后一种解决方案将需要其他应用程序逻辑来忽略'已删除'的记录”时,简单的解决方案是拥有一个将它们过滤掉的视图。评论
这不仅仅是一个观点问题。在集合上执行的任何操作都必须排除“已删除”记录。
–阿比
11年1月16日在20:45
#7 楼
与Spredzy建议的类似,我们在所有应用程序中都使用时间戳字段进行删除。布尔值是多余的,因为设置了时间戳记表明该记录已被删除。这样,除非模型明确要求包括删除的记录,否则我们的PDO总是将AND (deleted IS NULL OR deleted = 0)
添加到select语句中。如果对记录进行了很好的规范化,则空间很小,索引deleted
字段对选择速度的影响有限。#8 楼
您也可以将责任放在用户(和开发人员)上,并依次显示“您确定吗?”,“您确定吗?和“您是绝对,肯定和确定吗?”删除记录之前的问题。轻度有趣,但值得考虑。#9 楼
我过去经常在表行中看到像“ DeletedDate”这样的列,但我不喜欢它们。 “删除”的概念是,该输入不应该放在第一位。实际上,它们无法从数据库中删除,但我不希望它们与我的热数据一起使用。根据定义,逻辑上删除的行是冷数据,除非有人特别希望看到已删除的数据。此外,编写的每个查询都必须专门排除它们,索引也需要考虑它们。 />
我想看到的是数据库体系结构级别和应用程序级别的更改:创建一个名为“已删除”的架构。每个用户定义的表在“已删除”模式中具有相同的等效项,带有一个额外的字段来保存元数据-删除该表的用户以及何时删除该表。需要创建外键。
接下来,删除变为插入删除。首先,将要删除的行插入其“已删除”架构对应项中。然后可以删除主表中的相关行。但是,确实需要在该行的某个位置添加额外的逻辑。可以处理违反外键的问题。
必须妥善处理外键。逻辑上删除一行,但其主/唯一在其他引用该表的列中是不好的做法。无论如何这都不应该发生。常规作业可以删除寡妇行(尽管存在外键,但其主键在其他表中没有引用的行。但是,这是业务逻辑。
总体好处是减少了表中的元数据并带来了性能提升。 “ deletedDate”列表示该行实际上不应该在此处,但是为了方便起见,我们将其保留在此处并让SQL查询对其进行处理。如果已删除行的副本保留在“已删除”模式中,则包含热数据的主表将具有较高百分比的热数据(假设及时将其存档),并且不必要的元数据列会更少。索引和查询不再需要考虑此字段。行大小越短,页面上可以容纳的行越多,SQL Server可以工作的越快。
主要缺点是操作的大小。现在有两种操作,而不是一种,还有额外的逻辑和错误处理。与更新否则要花费的更新单个列相比,它可以导致更多的锁定。事务在表上的锁定时间更长,并且涉及两个表。至少以我的经验,删除生产数据很少完成。即便如此,在主表之一中,近1亿个条目中有7.5%的'DeletedDate'列中有一个条目。 。只需按照相反的顺序进行操作:将“已删除”模式中的行插入主表,然后从“已删除模式”中删除行。再次需要一些额外的逻辑和错误处理,以确保避免错误,外键问题等。
评论
我忘了提起第二种情况,在经过一段合理的时间后,需要删除或移动标记的记录。您正在使用什么数据库?
临时表是SQL Server 2016及更高版本的最佳解决方案。