【问题标题】:Is there a best practice for maintaining history in a database?是否有在数据库中维护历史的最佳实践?
【发布时间】:2010-05-20 15:26:11
【问题描述】:

我不经常做数据库工作,所以这对我来说完全是陌生的领域。

我有一张表,里面有很多用户可以更新的记录。但是,我现在想保留他们更改的历史记录,以防万一他们想要回滚。在这种情况下,回滚不是数据库回滚,而更像是两周后当他们意识到自己犯了错误时恢复更改。区别在于我不能让交易完成这项工作。

目前的做法是使用单独的表,还是仅使用当前表中的标志?

这是一个小型数据库,5 个表,每个表

【问题讨论】:

标签: database-design


【解决方案1】:

从这个问题

How to keep history of record updates in MySQL?

保存版本历史的一个简单方法是创建一个基本相同的表(例如,带有 _version 后缀)。这两个表都有一个版本字段,对于主表,您每次更新都会增加该字段。版本表将在 (id, version) 上有一个复合主键。

每当您对实际表进行更新时,您还会在版本表中插入包含重复数据的新行。每当您想查找版本历史记录时,您所需要做的就是 SELECT * FROM content_version WHERE id = CONTENT_ID ORDER BY version。

如果您使用 Doctrine ORM 之类的东西,它的行为会通过事件侦听器自动为您执行此操作。你可以在这里查看:http://www.doctrine-project.org/documentation/manual/1_2/en/behaviors#core-behaviors:versionable

最简单的解决方案(取决于您的具体需求)可能是在您的表中添加一个 on update/insert/delete 触发器,这样您就可以在插入/更新/删除数据时执行额外的日志记录。这样即使是对数据库的手动干预也将被覆盖......

查看http://dev.mysql.com/doc/refman/5.1/en/triggers.html了解更多信息。

【讨论】:

  • 触发器是最好的处理方式,因为您永远无法保证数据只会从用户界面更改。
【解决方案2】:

Frustrated's answer 是一种解决方案 - 另一个是记录更改、何时以及由谁更改的审计表。这真的是一个方法的问题。如果您的应用程序表的性能至关重要,并且它们可以使用“活动行”方法大量增长,那么审计表会更好,因为它将历史记录与活动内容分开(我希望修复用户错误的次数更少比“正常”交易更常见)。

【讨论】:

  • 我会同意的。只需将审计信息分开即可。
【解决方案3】:

一种选择是让所有表都包含一个 ID 列和一个 VersionNo 列。然后插入一条具有相同 ID 和递增 VersionNo 的新记录,而不是运行 UPDATES。

然后,您可以为每个表创建多个视图,这些视图仅返回每个 ID 的最新版本,以使您的查询更易于管理。

【讨论】:

  • 我喜欢这个版本不知道。我只是希望它不会对我迄今为止的所有测试用例和代码造成重大改写(因为他们不使用视图)
  • 这将导致所有现有代码的重大返工,因为您将不得不要求当前的代码。但是,如果您这样做是一个视图并引用该视图而不是主表,则更改非常简单。如果可以,最简单的重构方法是更改​​表的名称,然后将视图命名为该表的原始名称。这不会破坏您现有的代码,并且您只会在您希望回滚某些内容时针对新表编写代码。
【解决方案4】:

我会推荐第二张桌子。一个超级简单的方法是复制每个表的架构和一个日期/时间戳列和“ModifiedByUserID”列,这些列将存储原始数据。

或者,如果用户没有同时修改同一行中的大量记录,您可以通过创建以下架构来节省空间并更好地了解正在发生的事情:

变更表

ChangeID 
[TABLE UNIQUE ID] 
UserName 
Field 
OldValue
NewValue 
CreatedDate

【讨论】:

    【解决方案5】:

    您可以使用时间戳来指示记录的创建时间,并使用状态指示器来指示记录是否为“活动”。

    当他们对记录进行更新时,将其副本作为新记录插入(触发器可以使时间戳保持最新),然后将旧记录状态设置为“存档”,将新记录状态设置为“活动”。如果需要,您还可以设置其他审计字段,例如“上次更新时间戳”、“存档日期”、“未存档日期”(用于回滚时)、“用户创建”、“用户上次修改”、.. . 如果你想要完整的审计历史,你需要一个单独的审计表。

    回滚只是选择一个版本并将其标记为“活动”,将其他版本标记为“存档”。

    还有其他更灵活但也更复杂的方案,但您可能不需要它们。

    【讨论】:

      【解决方案6】:

      这种方法怎么样?我有一个包含一些表的关系数据库,并在“堆栈”上构建带有时间戳的记录。

      例如:假设我创建了employee table + Car table 并且在第三个表中我将记录关系employee <-> car 每次更改时(带有日期)。当然,这应该由 GUI 支持,我可以从中选择将填充新表的记录的组件(员工和汽车):

      first record employee1 <-> car1 : 01/01/2013
      then         employee1  <-> car2: 01/06/2013
      

      如果我想知道历史,我就问这张桌子。无需更新任何随着变化而滚动的历史记录。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-11-10
        • 2011-10-03
        • 2010-10-18
        • 2018-04-21
        • 2018-08-26
        • 2010-10-20
        • 1970-01-01
        • 2023-02-19
        相关资源
        最近更新 更多