2008-11-27 72 views
122

假设我在数据库中有记录,并且管理员和普通用户都可以执行更新。如何版本控制数据库中的记录

任何人都可以提出一个很好的方法/架构如何进行版本控制在此表中的每一个变化,从而有可能回退记录到以前的版本。

回答

123

比方说,您有一个FOO表,管理员和用户可以更新。大多数情况下,您可以针对FOO表编写查询。快乐的时光。

然后,我将创建一个FOO_HISTORY表。它包含FOO表的所有列。主键与FOO加上RevisionNumber列相同。有一个从FOO_HISTORYFOO的外键。您也可以添加与修订相关的列,例如UserId和RevisionDate。在所有*_HISTORY表中(即从Oracle序列或等同物中)以不断增加的方式填充RevisionNumbers。不要只依赖于一秒钟的变化。即。不要把RevisionDate放入主键。

现在,每次更新FOO时,就在更新之前,将旧值插入FOO_HISTORY。你在设计的某个基本层次上这样做,以便程序员不会意外地错过这一步。

如果你想从FOO删除一行,你有一些选择。可以级联和删除所有历史记录,也可以通过将FOO标记为已删除来执行逻辑删除。

如果您对当前值非常感兴趣并且只是偶尔在历史记录中感兴趣,则此解决方案非常有用。如果您始终需要历史记录,那么您可以输入有效的开始和结束日期,并将所有记录保存在FOO中。每个查询都需要检查这些日期。

35

我认为你正在寻找版本的数据库中记录的内容(如StackOverflow上做当有人编辑提问/回答)。一个好的起点可能是看一些使用修订版跟踪的数据库模型。

想到最好的例子是维基百科引擎MediaWiki。比较数据库图here,特别是revision table

根据你使用的是什么技术,你必须找到一些很好的差异/合并算法。

检查this question是否适用于.NET。

21

在BI世界里,你可以通过添加的startDate和结束日期到要版本表做到这一点。将第一条记录插入表中时,会填充startDate,但endDate为空。当您插入第二条记录时,还会使用第二条记录的startDate更新第一条记录的endDate。

当您要查看当前的记录,你选择一个地方结束日期为空。

这有时被称为2型Slowly Changing Dimension。 另请参阅TupleVersioning

+0

使用这种方法,我的桌子不会变得很大吗? – 2008-11-27 07:32:09

+1

是的,但您可以通过对表格进行索引和/或分区来处理该问题。而且,只有少量的大桌子。大多数将会小得多。 – ConcernedOfTunbridgeWells 2008-11-27 12:46:26

3

你不说什么数据库,我没有看到它在后标签。如果是Oracle的话,我可以推荐在Designer中内置的方法:使用journal tables。如果是用于任何其他数据库,那么我基本上也推荐同样的方法...

它的工作方式,如果你想在另一个数据库中复制它,或者如果你只是想了解它,对于一个表来说,还有一个影子表,它只是一个普通的数据库表,具有相同的字段规格,以及一些额外的字段:如上次采取的操作(字符串,用于插入的典型值“INS”,“UPD”用于更新,“DEL”用于删除),操作发生时的日期时间和用户ID。

通过触发器,对表中任何行的操作都会在日志表中插入一个新行,其中包含新值,采取何种操作,何时以及由什么用户执行。你永远不会删除任何行(至少不是在过去的几个月)。是的,它会变得很大,很容易数百万行的,但你可以很容易地跟踪任何纪录时间任何点以来的日志开始或旧杂志排最后得到了净化,谁最后一次修改的值。

在你需要的是为SQL代码自动生成,所有你需要做的就是编译/运行甲骨文的一切;它附带了一个基本的CRUD应用程序(实际上只有“R”)来检查它。

3

两个选项:

  1. 有一个历史表 - 插入新的数据到这个历史表只要原来的更新。
  2. 审计表 - 存储值之前和之后的值 - 仅用于审计表中的修改列以及其他信息,如谁更新和何时更新。
2

您可以通过SQL触发器在SQL表进行审核。从触发器可以访问2个特殊表格(inserted and deleted)。这些表格包含每次更新表格时插入或删除的确切行。在触发器SQL中,您可以将这些修改后的行插入到审计表中。这种方法意味着您的审计对程序员来说是透明的;不需要他们的努力或任何实施性知识。

这种方法的好处是,审计将无论通过数据访问的DLL发生的SQL操作发生,或通过手动的SQL查询; (因为审计是在服务器本身上执行的)。

7

升级到SQL 2008

尝试使用SQL变更跟踪,在SQL 2008年而不是时间戳和墓碑列的黑客,你可以用在你的数据库中的数据跟踪更改这一新功能。

MSDN SQL 2008 Change Tracking

2

我也是做同样的事情。我正在为课程计划制作一个数据库。这些计划需要原子更改版本灵活性。换句话说,无论教学计划多么小,每个变化都需要被允许,但旧版本也需要保持不变。这样,课程创建者可以在学生使用课程时编辑课程计划。

它的工作方式是,一旦有学生做了教训,他们的结果连接到他们完成的版本。如果进行更改,他们的结果将始终指向他们的版本。

这样,如果一节课的标准被删除或移动,其结果不会改变。

我目前这样做的方式是处理一个表中的所有数据。通常我只有一个id字段,但是在这个系统中,我使用了一个id和一个sub_id。 sub_id始终保留在行中,通过更新和删除。该id是自动递增的。课程计划软件将链接到最新的sub_id。学生成绩将链接到ID。我还包含一个时间戳记用于跟踪发生更改的时间,但没有必要处理版本控制。

我可能会改变一件事,一旦我测试了它,我可能会使用前面提到的endDate null的想法。在我的系统中,为了找到最新版本,我必须找到max(id)。其他系统只是查找endDate = null。不知道是否有优惠outway有另一个日期字段。

我的两分钱。

2

@WW。答案是一个很好的答案另一种方法是制作一个版本列,并将所有版本保存在同一个表中。

为一个表的方法您:

  • 使用标志指示最新ALA Word Press
  • 或做比outer join版本讨厌更大。

outer join使用方法的版本号的一个例子SQL是:

SELECT tc.* 
FROM text_content tc 
LEFT OUTER JOIN text_content mc ON tc.path = mc.path 
AND mc.revision > tc.revision 
WHERE mc.revision is NULL 
AND tc.path = '/stuff' -- path in this case is our natural id. 

坏消息是上述需要outer join和外连接可能会很慢。好消息是创建新条目在理论上更便宜,因为您可以在中执行一次写入操作,但不支持事务(假设您的数据库是原子的)。

一个例子做一个新的修订版'/stuff'可能是:

INSERT INTO text_content (id, path, data, revision, revision_comment, enabled, create_time, update_time) 
(
SELECT 
(md5(random()::text)) -- {id} 
, tc.path 
, 'NEW' -- {data} 
, (tc.revision + 1) 
, 'UPDATE' -- {comment} 
, 't' -- {enabled} 
, tc.create_time 
, now() 
FROM text_content tc 
LEFT OUTER JOIN text_content mc ON tc.path = mc.path 
AND mc.revision > tc.revision 
WHERE mc.revision is NULL 
AND tc.path = '/stuff' -- {path} 
) 

我们插入使用旧数据。如果说你只想更新一列并避免乐观锁定和/或事务,这是特别有用的。

标记方法和历史表方法需要插入/更新两行行。

修订版编号方法的另一个优点是您可以随后使用触发器重构多表格方法,因为触发器本质上应该执行上述操作。

5

只是想补充一点,这个问题的一个很好的解决方案是使用Temporal database。许多数据库供应商开箱即用或通过扩展提供此功能。我已经成功使用PostgreSQL的temporal table扩展,但其他人也有。无论何时更新数据库中的记录,数据库都会保留该记录的先前版本。