【问题标题】:What is the best way to implement soft deletion?实现软删除的最佳方法是什么?
【发布时间】:2010-09-09 06:14:15
【问题描述】:

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

对于现在的未来维护,每个SELECT 查询都需要确保它们不包含记录where is_deleted='1'

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

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

【问题讨论】:

    标签: sql database database-design backup


    【解决方案1】:

    我倾向于deleted_at 列,其中包含删除发生的日期时间。然后你会得到一些关于删除的免费元数据。对于您的SELECT,只需获取行WHERE deleted_at IS NULL

    【讨论】:

    • 我们就是这样做的——删除总是一个日期字段
    • +1,因为它包含与“最佳答案”中提供的解决方案完全相同的信息,并且还为您提供删除发生日期的信息。
    • 在这种情况下索引deleted_at是否有益?
    • 我认为使用日期字段比使用标志更好,因为您可以通过多种方式对日期字段进行分区。
    • 同意!!! IMO,使用 datetime 帮助我们更多地了解删除时间。此外,它使我们的代码具有可读性并具有一些命名约定,例如:created_at、updated_at、deleted_at、created_by、updated_by、deleted_by。
    【解决方案2】:

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

    【讨论】:

    • 第二个。这听起来非常适合视图。
    • 不要忘记索引该列;)
    • 也考虑按该键进行分区,这应该比对选择性低的列上的简单索引产生更大的积极影响。
    【解决方案3】:

    拥有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 行(两个分区)。

    【讨论】:

    • 我希望我可以多次投票,很好,很简单,它只是解决了我遇到的一个问题
    • -1 为什么分区不是表或视图?是否真的有必要再引入一个概念;甲骨文不是已经拥有与国会图书馆相媲美的文献量吗?
    【解决方案4】:

    如果表很大并且性能存在问题,您可以随时将“已删除”记录移动到另一个表中,其中包含删除时间、删除记录等附加信息

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

    【讨论】:

    • 这几乎得到了公认的答案。有些表会很大(ish),我没有想到性能问题。如果没有审计数据库,我肯定会采用这种方法。
    • 甜...不过,这真的不是为了给我积分...如果它有用,那才是真正重要的。很高兴您发现答案有帮助:)
    • 我真的很喜欢这个想法,因为这样我的主表就不会被成千上万的软删除记录弄乱了。但是,您将如何处理孩子和受抚养人的记录?你会基本上重新创建相同的表依赖项(例如订单和订单项)吗?
    • 如果性能有问题,您可以按 is_deleted 或 deleted_at 或任何方式对表进行分区以加快查询速度。
    【解决方案5】:

    遗憾的是,最佳响应取决于您尝试通过软删除实现的目标以及您在其中实现此功能的数据库。

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

    如果您有大量数据,您需要考虑通过数据库本身或通过两个单独的表(例如 Products 和 ProductHistory)或通过索引视图对数据进行分区。

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

    【讨论】:

    • 我同意,“deleted_at”比简单的“is_deleted”更可取,因为您可以免费获得额外信息。
    【解决方案6】:

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

    你希望能够:

    • 知道那里有哪些信息(在删除之前)?
    • 知道它是什么时候被删除的吗?
    • 知道是谁删除的吗?
    • 知道他们删除它时是以什么身份行事的吗?
    • 能否取消删除记录?
    • 能否知道它何时被取消删除?

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

    【讨论】:

      【解决方案7】:

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

      想想循环:

      1. 创建用户(登录=JOE)
      2. 软删除(将已删除的列设置为非空。)
      3. (重新)创建用户(登录=JOE)。错误。 LOGIN=JOE 已被占用

      第二次创建导致违反约束,因为 login=JOE 已经在软删除的行中。

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

      我自己的意见是 +1 移动到新表。它需要很多 遵守 *AND delete_at = NULL* 的纪律 查询(适用于您的所有开发人员)

      【讨论】:

      • 嗨,安迪,我正好遇到了你的情况。但我不认为移动到另一张桌子有利于长期维护。选项 2 似乎更好.. 你为什么不推荐它?我只是好奇。
      • 还有另一种删除行的方法,您将唯一键复制到软删除列(相同类型)并将原始键设为空。
      • @NicholasPipitone 为什么不呢?请注意,我们对 (login, delete_at_timestamp) 进行了约束,而不是 (login, is_deleted)
      • @HoàngLong 啊,我明白了,在这种情况下是 nvm。这是可能的。
      • @HoàngLong 我刚刚对其进行了测试,但它不起作用 - 但出于一个有趣的原因。显然 MySQL 不对 NULL 列强制执行唯一约束,只有非 NULL 列。所以选项 2 最终不起作用。
      【解决方案8】:

      如果您像 Jim 所说的那样将已删除的数据移动到另一个表中,并记录删除的时间、原因以及由谁删除,您肯定会获得更好的性能。

      在所有查询中添加where deleted=0 会显着减慢它们的速度,并阻碍使用表上可能拥有的任何索引。尽可能避免在表格中使用“标志”。

      【讨论】:

        【解决方案9】:

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

        【讨论】:

          【解决方案10】:

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

          扩展性很好,并且以数据为中心,保持数据占用空间非常小 - 对于 350gb+ dbs 具有实时关注的关键。使用替代品、表、触发器会产生一些开销,具体取决于需要,也可能不适合您。

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

          【讨论】:

            【解决方案11】:

            我更喜欢保留一个状态列,这样我就可以将它用于几个不同的配置,即已发布、私有、已删除、needsAproval...

            【讨论】:

              【解决方案12】:

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

              【讨论】:

                【解决方案13】:

                使用检查is_deleted = 0的视图、函数或过程;即不要直接在表格上选择,以防以后由于其他原因需要更改表格。

                并为更大的表索引is_deleted 列。

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

                【讨论】:

                  【解决方案14】:
                  @AdditionalCriteria("this.status <> 'deleted'")
                  

                  把它放在你的@entity上面

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

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-10-03
                    • 2015-06-05
                    • 2015-08-21
                    • 1970-01-01
                    • 2010-10-16
                    • 1970-01-01
                    相关资源
                    最近更新 更多