56

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

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

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

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

4

14 回答 14

101

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

于 2008-09-16T00:50:42.990 回答
54

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

于 2008-09-16T00:49:19.310 回答
22

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

于 2008-09-16T01:01:14.727 回答
15

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

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

于 2008-09-16T00:49:49.217 回答
15

遗憾的是,最好的响应取决于您尝试通过软删除来完成什么以及您在其中实现此功能的数据库。

在 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 字段为您自己和继承您的应用程序的人提供了额外的意义。而且我避免使用像瘟疫这样的位掩码字段,因为它们需要了解位掩码是如何构建的才能理解任何含义。

于 2008-09-16T12:47:20.113 回答
12

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

您是否希望能够:

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

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

于 2008-09-16T00:57:24.003 回答
10

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

想想循环:

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

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

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

我自己的意见是 +1 移动到新表。在所有查询中维护 *AND delete_at = NULL* 需要大量的纪律(对于所有开发人员)

于 2011-05-04T14:16:18.183 回答
5

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

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

于 2008-09-16T00:57:50.757 回答
2

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

于 2008-09-16T14:15:30.400 回答
1

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

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

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

于 2008-09-16T05:31:44.153 回答
0

我更喜欢保留一个状态列,所以我可以将它用于几个不同的配置,即发布、私有、删除、需要批准...

于 2008-09-16T01:09:30.370 回答
0

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

并为更大的表索引is_deleted列。

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

于 2008-09-16T03:25:48.247 回答
0

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

于 2011-06-27T11:15:50.090 回答
-1
@AdditionalCriteria("this.status <> 'deleted'")

把它放在你的上面@entity

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

于 2014-05-21T14:26:36.643 回答