实现软删除的最佳方法是什么?

时间:2008-09-16 00:47:55

标签: sql database database-design backup

目前正在处理项目,我们必须为大多数用户(用户角色)实施软删除。我们决定在数据库中的每个表上添加一个“is_deleted ='0'”字段,如果特定用户角色点击特定记录上的删除按钮,则将其设置为“1”。

为了将来的维护,每个SELECT查询都需要确保它们不包含is_deleted ='1'的记录。

是否有更好的解决方案来实现软删除?

更新:我还应该注意,我们有一个审计数据库,可以跟踪应用程序数据库中所有表/字段的更改(字段,旧值,新值,时间,用户,IP)。

14 个答案:

答案 0 :(得分:81)

我倾向于使用deleted_at列的“Rails方式”,其中包含删除发生时的日期时间。然后,您将获得有关删除的一些免费元数据。对于SELECT,只需获取行WHERE deleted_at IS NULL

答案 1 :(得分:45)

您可以针对包含WHERE IS_DELETED='0'子句的视图执行所有查询。

答案 2 :(得分:20)

拥有is_deleted列是一种相当不错的方法。 如果是在Oracle中,为了进一步提高性能,我建议通过在is_deleted列上创建列表分区来对表进行分区。

,然后删除和未删除的行将物理上位于不同的分区中,但对于您来说它将是透明的。

因此,如果您输入类似

的查询
SELECT * FROM table_name WHERE is_deleted = 1

然后Oracle将执行“分区修剪”并仅查看相应的分区。在内部,分区是一个不同的表,但作为用户,它对您来说是透明的:无论是否分区,您都可以在整个表中进行选择。但Oracle将能够仅查询所需的分区。例如,假设您有1000行is_deleted = 0和100000行is_deleted = 1,您可以在is_deleted上对表进行分区。现在,如果你包括条件

WHERE ... AND IS_DELETED=0

然后Oracle将仅扫描1000行的分区。如果表没有分区,则必须扫描101000行(两个分区)。

答案 3 :(得分:13)

如果表格很大并且性能有问题,您可以随时将“已删除”记录移至另一个表,其中包含删除时间,删除记录等其他信息等。

这样您就不必在主表中添加另一列

答案 4 :(得分:13)

遗憾的是,最好的回复取决于您尝试使用软删除以及您在其中实现此目的的数据库。

在SQL Server中,最佳解决方案是使用类型为SMALLDATETIME或DATETIME的deleted_on / deleted_at列(取决于必要的粒度)并使该列可以为空。在SQL Server中,行标题数据包含表中每个列的NULL位掩码,因此执行IS NULL或IS NOT NULL比检查存储在列中的值要快一些。

如果您拥有大量数据,则需要通过数据库本身或通过两个单独的表(例如Products和ProductHistory)或通过索引视图来查看数据分区。

我通常会避免像is_deleted,is_archive等标志字段,因为它们只带有一个含义。可以为null的deleted_at,archived_at字段为您自己和继承您的应用程序的人提供了额外的意义。而且我避免像瘟疫这样的位掩码字段,因为它们需要了解如何构建位掩码以便掌握任何含义。

答案 5 :(得分:10)

这取决于您需要哪些信息以及您希望支持哪些工作流程。

您希望能够:

  • 知道有哪些信息(删除之前)?
  • 知道什么时候被删除了?
  • 知道是谁删除了它?
  • 知道他们删除它时的行为能力是什么?
  • 能够取消删除记录吗?
  • 能告诉它何时被删除?

如果记录被删除和未删除四次,是否足以让您知道它当前处于未删除状态,或者您是否希望能够分辨出过去发生的事情(包括任何连续删除之间的编辑!)?

答案 6 :(得分:6)

小心软删除的记录导致违反唯一性约束。 如果您的数据库具有唯一约束的列,请注意先前的软删除记录不会阻止您重新创建记录。

想想周期:

  1. 创建用户(login = JOE)
  2. 软删除(将已删除的列设置为非null。)
  3. (重新)创建用户(login = JOE)。错误。已登录LOGIN = JOE
  4. 第二次创建导致约束违规,因为login = JOE已经在软删除行中。

    一些技巧:  1.将删除的记录移动到新表。  2.在login和deleted_at时间戳列

    中设置唯一性约束

    对于搬到新桌子,我自己的意见是+1。它需要很多     纪律保持你所有的* AND delete_at = NULL *     查询(适用于所有开发人员)

答案 7 :(得分:3)

如果您将已删除的数据移动到Jim所说的另一个表格中,并且记录删除的时间,原因以及由谁发送,您肯定会有更好的表现。

在您的所有查询中添加where deleted=0会大大降低速度,并阻碍您在桌面上使用任何索引。尽可能避免在表格中使用“标志”。

答案 8 :(得分:1)

我在项目中使用的东西是statusInd tinyint not null default 0 column 使用statusInd作为位掩码允许我执行数据管理(删除,存档,复制,恢复等)。在视图中使用它,然后我可以为消费应用程序进行数据分发,发布等。如果性能是关于视图的问题,请使用小事实表来支持此信息,删除事实,删除关系并允许删除删除。

可以很好地扩展并且以数据为中心,保持数据占用空间非常小 - 对于350gb + dbs而言,实时关注的关键。使用替代方案,表,触发器有一些开销,根据需要可能或可能不适合您。

与SOX相关的审核可能需要不止一个字段来帮助您,但这可能会有所帮助。 享受

答案 9 :(得分:1)

你没有提到什么产品,但SQL Server 2008和postgresql(以及其他我确定的)允许你创建过滤索引,所以你可以创建一个覆盖索引,其中is_deleted = 0,减轻了一些负面因素这种特殊的方法。

答案 10 :(得分:0)

我更喜欢保留状态列,因此我可以将它用于几种不同的配置,即已发布,私有,已删除,needsAproval ......

答案 11 :(得分:0)

使用检查is_deleted = 0的视图,函数或过程,即如果表因其他原因需要稍后更改,请不要直接在表上选择

并为更大的表<_p>索引is_deleted列

由于您已有审计跟踪,因此跟踪删除日期是多余的

答案 12 :(得分:0)

创建其他架构并将其全部授予您的数据架构。 在新模式上实现VPD,以便每个查询都有谓词,允许选择仅附加到其上的未删除行。 http://download.oracle.com/docs/cd/E11882_01/server.112/e16508/cmntopc.htm#CNCPT62345

答案 13 :(得分:0)

@AdditionalCriteria(&#34; this.status&lt;&gt;&#39;已删除&#39;&#34;)

将此放在@entity

之上

http://wiki.eclipse.org/EclipseLink/Examples/JPA/SoftDelete