软删除是一个好的想法吗?

136

软删除是个好主意还是坏主意?

与其在数据库中真正删除一条记录,你可以将其标记为IsDeleted = true,当恢复该记录时,只需将其标记为False

这是个好主意吗?

是否更好的做法是物理删除记录,然后将其移到归档数据库中,如果用户需要该记录,则软件会在归档中查找记录并重新创建它?


17
他们使用碎纸机。 - Omar
30
使用删除时间戳而不是标记。 - Dave Jarvis
13
我曾参与开发一个应用程序,该应用程序在表格上使用了一个 IsDeleted 属性。结果频繁出现错误。问题很明显:每个普通用户想要针对该表运行的查询都涉及到“未删除”的数据,这意味着几乎99.9%的查询涉及到该表都必须在它的 WHERE 子句中添加 ...AND IsDeleted = 'N'。自然而然,这常常被省略:不论是编写代码的人忘记了添加它,还是根本不知道他们必须首先添加它。当然,在原始规范中没有这个要求,编写代码的人是在使用他们的主观判断力… - onedaywhen
2
@onedaywhen - 你所描述的问题可以通过使用“视图”轻松解决。如果之后添加了“IsDeleted”,甚至可以将原始表重命名为“mytable_all”,然后将视图命名为“mytable”,并添加另一个名为“mytable_deleted”的视图。这样就不需要更改对表的任何引用。 - tuomassalo
1
@blasto:使用审计表和审计列(已删除、已创建、已更新以及由谁操作)来跟踪行被删除的原因。根据问题域,了解数据被删除的原因可能并不相关。如果相关,则添加一个注释列:审计表将跟踪修订历史记录。 - Dave Jarvis
显示剩余2条评论
15个回答

100

我认为通常来说这是个不好的想法(也许有一些例外)。

首先,你的数据库应该定期备份,因此您永远不应该因为一次DELETE操作而永久性地丢失数据(当然,如果只是删除刚添加的数据,则除外)。

其次,像这样进行软删除意味着您现在必须在该表上的每个查询中包含一个WHERE IsDeleted = false子句(如果您正在连接这些表,则情况会更糟)。 错误出现时,只有当用户或测试人员注意到已删除的记录再次显示时才会被捕获,这可能需要一些时间。此外,开发人员很容易忘记将WHERE子句从COUNT(*)查询中省略,这可能需要更长时间才能发现(我曾经参与过一个这样的项目,在此项目中,这种情况发生了多年;由于没有太多记录被“删除”,因此总数接近预期,但没有人注意到)。

最后,软删除将适用于具有人工主键的表,但可能无法适用于具有自然主键的表(例如,您从以社会保障号为关键字的表中“删除”某人-当您需要再次添加他时,该怎么办?请不要说“在复合主键中包含IsDeleted”)。

在设计审查中,我希望开发人员能够展示出对成本和收益的认识,并提出执行软删除的一个极好理由。 “为什么不这样做?”不是一个优秀的理由。


6
如果需要,创建一个视图并将其实体化排除所有已删除的记录相当简单。然后可以对该视图运行所有查询。至于从备份中恢复——例如重新启用用户帐户之类的事情——你真的不想去归档中恢复他们的数据。你会希望将 is_deleted 置为 false。这是一种相当原始(且不太可能出现的)情况,因为你可能会使用“禁用”字段——但关键点仍然相同。对于某些情况,能够立即恢复已删除的数据可能很有价值。 - Josh Smeaton
40
@Josh: SQL编程中的每一个细节都相对简单;问题在于大量细小的事情的积累会导致问题。就像我在上一句话中所说的,我期望开发人员有充分的理由来增加系统的复杂性。然而,我感到失望但并不惊讶看到很多开发人员说他们会这样做,而不考虑其应用程序的实际需求。 - MusiGenesis
3
我不再同意在大多数情况下使用软删除的概念。“以防万一”的理由不是使用软删除的好理由。 - Josh Smeaton
6
值得一提的是:结合软删除使用的 UNIQUE 索引可能会导致混乱。例如,尝试注册一个已被软删除的用户名将会失败,即使它目前并未被使用。 - Danny Beckett
3
除非你确实有理由这样做,否则不要使用软删除。这里没有提到的是,软删除行会导致引用完整性失效。如果你标记一行为已删除,则应该将每个子行都标记为已删除,如果你不这样做,DBMS 将无法防止此类逻辑删除。因此,“不对级联删除产生问题”在这里不适用。那么为什么不直接删除整个 FK 呢?这还可以允许你在不检查引用记录的情况下删除行。 - Nicolai Shestakov
显示剩余9条评论

93

避免潜在的数据丢失从来都不是一个坏主意。

我通常采用软删除的方式。在需要清除一个或多个记录的数据库中,我通常采用两步法,首先进行软删除,然后清空"回收站"中的记录,或者采用文档管理风格方法,其中文档记录可以被归档,然后在硬删除之前经过审批流程。


4
系统通常仍需要数据以保持完整性、审计或更改历史记录...不要进行软删除!请使用清理流程进行真正的删除等操作...PK :-) - Paul Kohler
35
我总是进行软删除。 - KMån
3
为了避免在每个查询中检查where deleted=0,您可以为该表创建一个视图来代替。您可以创建一个名为"recycle_bin"的视图(通过对使用通用字段进行软删除的表执行 UNION 操作),以仅显示删除记录。 - Neil McGuigan
2
+1 代表使用安全的、混合的、两步骤过程。 - Michael M
@RobertHarvey 是的,我明白这一点,但如果我已经软删除了一条记录,并且该记录的ID是其他表的外键,那么该表需要更新该外键,对吧? - Primoz Rome
显示剩余2条评论

32

这取决于具体情况。我可以看到有些情况你需要合法地永久删除某些信息。比如说,有人要求从你的系统中永久删除他们的社保号码,或者你想将重复的记录整合成一条记录。保留一个带有已删除标记的副本可能并不是一个好主意。

同时还存在一个技术上的缺点:你无法进行级联删除,即自动清除任何对已删除数据的引用,以防止外键违规。虽然这不一定是个大问题,但需要谨记在心。

总的来说,我认为这是个不错的想法。


2
关于级联删除的观点很好。 - MusiGenesis
2
@UpTheCreek:我认为DanM的观点是,如果你正在执行软删除操作,那么你必须自己处理级联删除。如果你使用硬删除(我所说的“正常”),你可以让数据库自动执行级联删除。 - MusiGenesis
1
@MusiGenesis - 那级联恢复呢? - Travis J
4
好的,我会尽力进行翻译。以下是需要翻译的内容:+1 for starting with "It depends.."视情况而定。 - Michael M

24

如果您要使用软删除,建议使用 deleted_date 字段,而不是 is_deleted 字段。这样可以得到一个更有用的额外数据,而不仅仅是一个位字段。


3
使用 DateTime 字段而非 Bits,我给你点赞。老系统中需要管理 “IsSomething” 和 “DateSomethinged” 的数量非常令人烦恼。 - Keith Williams
我们最近被要求在表中直接创建isDeleted位作为列...需要管理它的值。我们之前在视图中有这个位,根据删除日期不为空且删除日期小于Getdate()的业务规则计算得出。现在我不情愿地遵从。 - Brandon Wittwer
@BrandonWittwer,为什么?如果你有一个first_name列和一个last_name列,你是否也会有一个full_name列,只是因为应用程序想要一个full_name属性,还是你会在应用程序中计算它?顺便说一下,deleteDate < Getdate()应该是多余的。此外,如果不知道你的后端情况,也许你可以实现一个计算列,这样你就不必自己维护两个单独的列的完整性了。http://technet.microsoft.com/en-us/library/ms191250(v=sql.105).aspx - Josh Smeaton
@JoshSmeaton,你完美地表达了我的挫败感。可以完全由同一记录中的其他数据组成的数据应绝对是视图的一部分。如果需要材料化(作为性能考虑),则可以考虑计算列。当然,在这种情况下,计算不可能进行,因为其中的getDate()...是非确定性的。 - Brandon Wittwer
1
在技术上讲,当您可以延后生效日期时,一些状态的日期字段 < GetDate() 是必要的...例如:PublishDate = 三周后... 到那时 isPublished 为false。 - Brandon Wittwer

20

软删除的主要问题之一是这些不需要的数据可能会对数据库性能产生影响。几年前,我的一个客户要求我对所有数据库项进行软删除,我的解决方案是将所有“已删除”项目移动到备份表中,而不是留在当前正在运行的表中。


4
赞成将数据移动到另一张表格中! - scunliffe
15
如果可能的话,您可以通过“IsDeleted”对表进行分区,以减轻性能影响。这样,您就不必担心两个不同的表了。 - Michael Petito

17

如果无效删除会导致严重后果,而恢复操作应该很简单,则采用此方法是一个好主意。此外,如果你想跟踪所有曾经存在的内容,并且“删除”实际上只是“隐藏”,那么采用此方法也是一个不错的选择。这意味着,是否使用此方法取决于具体情况。


9

我不会试图在这方面“政治正确”。如果你支持软删除,那么你需要去做一次大脑检查。

1)首先,通过不删除表中的行,您到底实现了什么?只是因为有时将来可以访问这些行,对吧?那为什么不创建存档表并将行移动到那里呢?有什么问题吗?

2)使用软删除会在 is_active 或某个时间戳列上创建不必要的查询。这只是浪费,当您编写更简单的查询时就会遇到困难。是的,它可以在视图中工作,但视图不是额外的附属品吗?每个视图都是额外的 SQL,额外的性能成本,在任何商业 RDBMS 中,所有内容都是表。除了您不知道如何在表之上编写查询之外,没有任何神奇之处。

3) 是的,它可以在视图或 MV 中工作。但是,我已经在生产中看到了执行 FTS 的查询,并且一切仍然正常!现代硬件和可靠的软件的奇迹。但是,这也不意味着它是正确的。所以按照同样的逻辑,仅仅因为它有效并不意味着它是正确的。

4) 软删除的复杂性永远不会停留在简单的选择上。

A) 假设您有一个唯一约束条件。现在您软删除了一行,但具有唯一约束条件的列仍然存在。当您想要重新添加相同的数据时,您无法这样做而不使用其他“技巧”。

B) 您可能拥有从表 A 到表 B 的关联,当您从表 A 中软删除某些内容时,您需要确保对表 B 上的独立查询考虑到了这一事实。假设典型的详细页面正在处理某个详细 ID。现在,master_id 已经被软删除,但是您仍然可以在各个地方看到带有该 master_id 的 detail_id 的永久链接。当您在 master_id 上进行硬删除时,这些详细信息将不再存在。现在,通过软删除,它们仍然存在,并且必须意识到其 master_id 处于软删除模式。

它不会停留在 Table_A.is_active = 0 或 1 阶段。

5) 进行硬删除是简单且正确的。

A) 没有人需要在任何地方添加任何额外的东西或担心任何问题。

  1. 您的应用程序逻辑更简单
  2. 您的数据库更小
  3. 您的查询更快

只需存档数据和相关部分,您就可以做到这一点。


13
那是非常武断的答案。如果没有每种情况的全部细节,你怎么能得出绝对永远不应使用软删除的结论?切勿轻言放弃,这表明你缺乏想象力。 - Michael M
3
不分享你的细节,你不是在做出假设吗?软删除并不好,我坚持我的说法。分享你的案例,我会指出你的缺陷。 - rjha94
7
我觉得这个页面上的普遍共识显示你是孤立的。这里的人不仅记录了可以使用它的不同场景,还提供了使它有用的实用方法。如果你选择建议所有这些人都需要“脑部检查”,那是由你来决定,同时也反映了你自己。 - Michael M
4
硬删除并非唯一选择。在某些情况下,您不能只是硬删除记录。有时候,即使记录已被删除,我也需要被删除记录的引用(链接到另一个表中),这可以通过软删除来实现。 将记录移动到“存档表”中并不像您描述的那样简单,它不会仅仅停留在从A到B的简单移动上。 - Wanny Miarelli
2
曾经有一个普遍的共识是太阳绕地球转,而那些说反话的人会被烧死。如果你真的认为软删除是一个好主意,那么你在一段时间内没有管理过任何复杂的数据库。通过软删除所要实现的任何目标也可以以一种干净的方式在没有软删除的情况下实现。到目前为止,人们只是抱怨过于“自以为是”,但没有提供任何数据。软删除是解决问题的“懒惰”方法,应该使用归档来解决。如果你不需要这个表中的数据,为什么还要让它挂在那里呢? - rjha94
显示剩余5条评论

8
软删除还可以允许您从应用程序使用的数据库帐户中撤销DELETE权限。请参考revoke

叹气 应用程序使用的数据库账户?那太好了。 - aehiilrs
12
除了人为错误之外,这并没有什么实际用途。如果我是一个恶意用户并获得了访问权限,我仍然可以通过执行“UPDATE table SET Field1 = 0, Field2 = 0, ... WHERE 1”来“删除”它们。 - Andreas Bonini

5
有时软删除是必要的。例如,假设您有一个引用产品表的发票表。一旦您使用特定产品创建了发票,您就永远无法删除该产品(如果您的RI设置正确,则不会允许您这样做)。
这种情况假定您永远不想删除发票,在真实公司中,您可能不想删除历史财务数据。
虽然还有许多其他情况,由于链条上方的依赖关系无法删除,因此您将无法删除某些数据,原因可能是业务或其他原因。

2
这不更像是“暂停”而不是“删除”吗?因此,我认为当您需要历史数据时,“暂停”记录是适当的,而当您不需要历史数据(或轻松撤消删除)时,“删除”记录是适当的。 - Paul Fleming
1
非常正确。我根据实际实现来销售这个想法,可能会将字段命名为“hidden_on”(时间戳)和“hidden_by”(用户ID)。这是因为记录只应该对最终用户隐藏,而不是管理员。对于管理,所有记录仍然必须像往常一样可见,除了对于用户而言被“隐藏”(删除)的记录将被标记为管理员所知。如果我的数据库中每个标准表都有一个存档表,那么我的架构将扩大一倍。噫。 - Michael M

4

这取决于数据。由于法律/审计要求,有些数据无法删除。

另一方面,社交网络网站应该提供一个选项来删除帐户及其所有相关数据,包括联系信息、照片、消息等。如果他们不提供,例如 Facebook,这将是一个真正的麻烦。


2
由于法律要求,一些数据必须被删除。 - Thilo
好的观点。没有想到那个。 - armandino
3
由于非法要求,一些数据必须被删除。 :) - MusiGenesis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接