【发布时间】:2010-05-20 15:26:11
【问题描述】:
我不经常做数据库工作,所以这对我来说完全是陌生的领域。
我有一张表,里面有很多用户可以更新的记录。但是,我现在想保留他们更改的历史记录,以防万一他们想要回滚。在这种情况下,回滚不是数据库回滚,而更像是两周后当他们意识到自己犯了错误时恢复更改。区别在于我不能让交易完成这项工作。
目前的做法是使用单独的表,还是仅使用当前表中的标志?
这是一个小型数据库,5 个表,每个表
【问题讨论】:
标签: database-design
我不经常做数据库工作,所以这对我来说完全是陌生的领域。
我有一张表,里面有很多用户可以更新的记录。但是,我现在想保留他们更改的历史记录,以防万一他们想要回滚。在这种情况下,回滚不是数据库回滚,而更像是两周后当他们意识到自己犯了错误时恢复更改。区别在于我不能让交易完成这项工作。
目前的做法是使用单独的表,还是仅使用当前表中的标志?
这是一个小型数据库,5 个表,每个表
【问题讨论】:
标签: database-design
从这个问题
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了解更多信息。
【讨论】:
Frustrated's answer 是一种解决方案 - 另一个是记录更改、何时以及由谁更改的审计表。这真的是一个方法的问题。如果您的应用程序表的性能至关重要,并且它们可以使用“活动行”方法大量增长,那么审计表会更好,因为它将历史记录与活动内容分开(我希望修复用户错误的次数更少比“正常”交易更常见)。
【讨论】:
一种选择是让所有表都包含一个 ID 列和一个 VersionNo 列。然后插入一条具有相同 ID 和递增 VersionNo 的新记录,而不是运行 UPDATES。
然后,您可以为每个表创建多个视图,这些视图仅返回每个 ID 的最新版本,以使您的查询更易于管理。
【讨论】:
我会推荐第二张桌子。一个超级简单的方法是复制每个表的架构和一个日期/时间戳列和“ModifiedByUserID”列,这些列将存储原始数据。
或者,如果用户没有同时修改同一行中的大量记录,您可以通过创建以下架构来节省空间并更好地了解正在发生的事情:
ChangeID
[TABLE UNIQUE ID]
UserName
Field
OldValue
NewValue
CreatedDate
【讨论】:
您可以使用时间戳来指示记录的创建时间,并使用状态指示器来指示记录是否为“活动”。
当他们对记录进行更新时,将其副本作为新记录插入(触发器可以使时间戳保持最新),然后将旧记录状态设置为“存档”,将新记录状态设置为“活动”。如果需要,您还可以设置其他审计字段,例如“上次更新时间戳”、“存档日期”、“未存档日期”(用于回滚时)、“用户创建”、“用户上次修改”、.. . 如果你想要完整的审计历史,你需要一个单独的审计表。
回滚只是选择一个版本并将其标记为“活动”,将其他版本标记为“存档”。
还有其他更灵活但也更复杂的方案,但您可能不需要它们。
【讨论】:
这种方法怎么样?我有一个包含一些表的关系数据库,并在“堆栈”上构建带有时间戳的记录。
例如:假设我创建了employee table + Car table 并且在第三个表中我将记录关系employee <-> car 每次更改时(带有日期)。当然,这应该由 GUI 支持,我可以从中选择将填充新表的记录的组件(员工和汽车):
first record employee1 <-> car1 : 01/01/2013
then employee1 <-> car2: 01/06/2013
如果我想知道历史,我就问这张桌子。无需更新任何随着变化而滚动的历史记录。
【讨论】: