软删除数据库记录(软删除)

7

故事

我将编写一些代码来管理应用程序中的已删除项目,但我将对它们进行软删除,以便在需要时可以返回它们。在隐藏或删除项目时,我的应用程序逻辑遵循一定的层次结构。

我在应用程序中逻辑地将我的项目放置在三个容器中:国家、城市、区域和品牌。 每个项目都应该属于一个国家、一个城市、一个区域和一个品牌。 现在,如果我删除了一个国家,它应该删除属于给定国家的城市、区域、品牌和项目。如果我删除了城市,它也应该删除其下的所有内容(区域、品牌等)。


注意

当我删除一个国家并删除相关品牌时,我应该注意到一个品牌可能在多个国家拥有物品。


问题

你建议

  1. 标记项目(无论是国家、城市、项目等)为已删除状态,这将需要大量的代码来检查每次从数据库加载任何项目时,是否已删除,还需要一些额外的字段来标记它所属的城市是否已删除,以及它所属的国家是否已删除等。

  2. 将已删除的内容移动到特定的表中(DeletedCountries、DeletedCities等),并保存它们所关联的项目的ID,以便稍后将它们插入回其原始表中。当然,这将节省我的应用程序所有管理已删除项目并确保删除所有层次结构的代码。

  3. 也许你有更好的方法/建议/想法来实现这样的事情!


2
由于层次结构问题,我实际上会选择不太优化的#2。它将为您节省编码痛苦,避免永久的where isdeleted<>1麻烦。(这可能会稍微加速SQL Server) - Earlz
7个回答

4

举个例子,解决方案#2(将已删除的项目移动到它们自己的表中)的一个优点是,如果你有很多记录,你就不必担心索引记录与其“删除”状态相关。

话虽如此,如果我要通过删除后插入的方式将数据从一个表移动到另一个表,我会确保在一个事务中完成。


那么 Dana,你认为我应该选择选项#2,但你的建议是在一个事务中执行插入和删除操作,你是说要有一个存储过程包含插入和删除语句? - Mazen Elkashef
的确。我这么说的原因是,如果您的代码在没有相应的“删除”(或反之亦然)的情况下进行“插入”,那么您的数据库将处于不一致状态。使用事务,您可以提交或回滚一系列 SQL 语句。该页面上的最后一个存储过程(http://www.4guysfromrolla.com/webtech/041906-1.shtml)使用 try/catch 的示例来说明此问题。 - dana
在删除品牌元素的情况下,如何处理引用完整性?即使DELETE和INSERT在单个事务中提交,外键引用也可能不存在,因此数据会容易出现不一致性。 - too

4

我现在使用一种技术,在我们的数据库中的每个用户维护的表上存储一个“DeleteDate”。DeleteDate字段是一个小日期时间数据类型,具有默认值6/1/2079。

通过对DeleteDate字段进行索引,我们可以使用标准视图或用户定义功能来仅返回“当前”记录(即那些删除日期在未来的记录)。所有查询在查找当前数据时都会通过此索引进行路由,并且删除变为一个简单的更新查询。

还需要进行一些相关的逻辑检查。但这是不必担心用户“意外”删除有价值数据的代价的一部分。

在将来,当这些表过于庞大且存在许多已删除的记录时,我们可以首先按照DeleteDate对表进行分区。这将使所有“删除”的记录与“活动”的记录分开。


+1 针对如何处理已删除的项目提出建议。我在当前项目中使用类似的方法。 - user166390
我对这个解决方案更多的信息很感兴趣,也许可以在博客文章中引用一个具体的示例之类的。我想了解更多关于这个解决方案如何随时间推移工作的信息,如何通过它来“路由[所有查询]”,以及它如何与想要查询和更新相同对象的ORM进行交互,而不是针对查询的视图和更新的表。 - flipdoubt

1

您应该通过将所有子项标记为已删除来管理层次结构。这样,如果您的产品属于某个品牌,您只需检查该品牌是否已删除即可。您还应该将逻辑放在数据检索方面,以避免收集不必要的已删除信息。

SELECT
  *
FROM
  products p,
  category c
WHERE
  p.catId = c.Id
  AND NOT c.Deleted

最重要的是,已删除类别的信息应该被索引。

CREATE PRIMARY INDEX ON category (Id)
CREATE INDEX ON category (Deleted)

或者

CREATE INDEX ON category (Id, Deleted)

但这将需要我为管理页面构建一个全新的DAL,它将返回所有数据(无论是否已删除)。 - Mazen Elkashef
你应该在设计数据访问层时考虑处理已删除列。如果这样做,唯一的区别是传递一个布尔标志来确定是否显示已删除元素。 - too
关于你的编辑,Id 已经是默认为索引的主键。所以我只需要将删除的部分建立索引!很抱歉这超出了本问题的范围,但是现在你让我感到有些困惑了! - Mazen Elkashef
你说得对,我刚刚修改了示例,代码只是为了说明这个想法,它不是特定于SQL Server的。 - too
我的意思是,我的数据库中的Id列已经是一个主键,这不意味着它已经在使用集群索引了吗?还需要为其添加索引吗?顺便说一下,感谢您提供的索引提示+1。 - Mazen Elkashef
如果列Id已经有索引,请不要添加 - CREATE PRIMARY INDEX/KEY只是为了说明在不创建多列索引时需要它。当您在应用程序中检查删除时,后者更可取。但显然在管理中您不这样做,因此为Id和Deleted分别建立单独的索引会更好。 - too

1

将项目标记为删除会使信息检索变得非常复杂,而且您需要自己处理级联删除。

我会选择“邮箱”方法,即将已删除的记录移动到不同的表中。我曾经做过一个使用软删除的项目,最终我把所有的删除调用都放在存储过程中,并在存储过程中处理复制和删除。


0

我认为标记项目是最好的方法,甚至我也使用邮件方法来实现软删除的目的。

是的,这需要考虑很多事情来管理,但我还没有找到其他方法。我只是在每个表中添加了一个额外的列,即状态,其数据类型为位。

谢谢


但是,每次显示之前检查项目是否已删除“每次”都会影响应用程序的性能!而且要管理我拥有的分层结构,它不仅仅会停留在单个列上,我还必须为层次结构较低的对象添加额外的列来检查其父项是否已被删除,因此它也应该被删除!..您能告诉我您对我的第二种方法的看法吗 :)?非常感谢您的时间! - Mazen Elkashef
是的,它会影响一些性能,但您可以根据应用程序的强度来管理性能。但我认为,如果您将记录添加到其他表中以进行条目删除,则会比这更加繁重。 - Bhavik Goyal
对不起,我不明白为什么有一个单独的表会增加负担!如果我删除一个项目,它就会被移动到另一个表中,当我显示它们时就不必检查这些项目了!当我移动大量的东西时,我会付出性能成本,但这种性能成本只会出现在我的面前(管理员),让业主面对一些延迟是可以接受的,以便让我的访问者获得更好的体验(更快的应用程序).. 我是否漏掉了什么或者想错了? - Mazen Elkashef

0
你需要什么样的删除技术?
如果只有一个日期字段且没有审计日志,您可以使用即时“已删除”标志。如果日期字段为空,则它尚未被删除。然后,您可以在索引上使用该日期字段(如果索引允许空值)。
如果您需要更复杂的内容,则可以使用额外的表格。您是否允许进行删除、取消删除、重新删除并保留每个操作记录?如果是这样,请为操作日志保留单独的表,并仅保留一个带有布尔字段的记录(实际上,在该表上的连接可能更快,这取决于数据)。

0

如果您经常重组项目,则标记是一种较好的方法,但最终需要更改数据访问以避免显示已标记的项目,如果您已经设置了大量访问数据的代码,则可能会非常痛苦,因此如果您有大量访问数据的“遗留”代码,则移动可能更好。如果这很少见,并且您还对历史记录感兴趣,则将其移动到另一个数据库表中效果很好。

实现任何一种方式的一种简单方法是使用触发器来更改删除行并执行操作。然而,如果您确实需要删除项目,则在标记而不是移动项目时,标记选项会变得非常麻烦。在许多情况下,触发器更容易的原因是您捕获每个删除,而不仅仅是由代码启动的删除。


被删除品牌的对象的引用完整性怎么处理? - too

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