如何版本控制数据库中的logging
假设我在数据库中有logging,pipe理员和普通用户都可以进行更新。
任何人都可以提出一个好的方法/体系结构如何版本控制这个表中的每一个变化,所以有可能回滚一个logging到以前的版本。
比方说,你有一个pipe理员和用户可以更新的FOO
表。 大多数情况下,您可以针对FOO表编写查询。 快乐的时光。
然后,我将创build一个FOO_HISTORY
表。 这有FOO
表的所有列。 主键与FOO加上RevisionNumber列相同。 有一个从FOO_HISTORY
到FOO
的外键。 您也可以添加与修订相关的列,例如UserId和RevisionDate。 在所有*_HISTORY
表(即从Oracle序列或等同物)中以不断增加的方式填充RevisionNumbers。 不要只依赖于一秒钟的变化。 即。 不要把RevisionDate
放到主键中。
现在,每次更新FOO
,在更新之前将旧值插入到FOO_HISTORY
。 你在devise的某个基本层次上做到这一点,这样程序员就不会意外地错过这一步。
如果你想从FOO
删除一行,你有一些select。 可以级联和删除所有历史logging,也可以通过将FOO
标记为已删除来执行逻辑删除。
当你对当前的价值很感兴趣时,这个解决scheme是很好的,只有在历史上偶尔才会有。 如果你总是需要历史,那么你可以把有效的开始和结束date,并保持所有logging在FOO自己。 每个查询都需要检查这些date。
我认为你正在寻找版本控制数据库logging的内容(因为当有人编辑问题/答案时,StackOverflow就会这样做)。 一个好的起点可能是看一些使用版本跟踪的数据库模型。
想到最好的例子是维基百科引擎MediaWiki。 比较这里的数据库图,特别是修订表 。
根据你使用的技术,你必须find一些很好的差异/合并algorithm。
检查这个问题,如果是.NET。
在BI世界中,您可以通过将startDate和endDate添加到要编译的表中来完成此操作。 将第一条logging插入表中时,startDate被填充,但endDate为空。 当您插入第二条logging时,还会使用第二条logging的startDate更新第一条logging的endDate。
当您想要查看当前logging时,请selectendDate为空的那个logging。
这有时被称为2型缓变尺寸 。 另见TupleVersioning
升级到SQL 2008。
尝试在SQL 2008中使用SQL更改跟踪。您可以使用此新function来跟踪数据库中数据的更改,而不是时间戳和逻辑删除列黑客。
MSDN SQL 2008更改跟踪
你不会说什么数据库,我没有看到后标签。 如果是Oracle,我可以推荐在Designer中内置的方法:使用日记表 。 如果是用于任何其他数据库,那么我基本上也推荐同样的方法。
它的工作方式,如果你想复制到另一个数据库,或者如果你只是想了解它,是一个表也有一个影子表也创build,只是一个普通的数据库表,具有相同的字段规格,加上一些额外的字段:比如最后一次采取什么操作(string,插入的典型值为“INS”,更新为“UPD”,删除为“DEL”),操作发生的date时间,它。
通过触发器,表中任何行的每个操作都会在日志表中插入一个新行,其中包含新值,采取何种操作,何时以及以何种用户进行操作。 你永远不会删除任何行(至less不是在过去几个月)。 是的,它会变得很大,很容易成千上万的行,但是你可以很容易地追踪任何logging的价值,因为从日志开始或旧的日记行上次被清除,谁做了最后的变更。
在Oracle中,所有你需要的东西都是自动生成的SQL代码,你所要做的就是编译/运行它。 并附带一个基本的CRUD应用程序(实际上只有“R”)来检查它。
两个选项:
- 有一个历史logging表 – 在原始数据更新时将旧数据插入到历史数据表中。
- 审计表 – 存储之前和之后的值 – 仅用于审计表中的修改列以及其他信息,如谁更新和何时。
您可以通过SQL触发器在SQL表上执行审计。 从触发器可以访问2个特殊的表格( 插入和删除 )。 这些表格包含每次更新表格时插入或删除的确切行。 在触发器SQL中,您可以将这些已修改的行插入到审计表中。 这种方法意味着你的审计对程序员是透明的; 不需要他们的努力或任何实施的知识。
这种方法的额外好处是无论SQL操作是通过数据访问DLL还是通过手动SQL查询来进行审计, (因为审计是在服务器上执行的)。
我也在做同样的事情。 我正在制定一个教案的数据库。 这些计划需要primefaces更改版本灵活性。 换句话说,每一次改变,无论多小,都需要被允许,但旧版本也需要保持不变。 这样,课程创build者可以在学生使用课程时编辑课程计划。
这样做的方式是,一旦学生上了一堂课,他们的成绩就会附在他们完成的版本上。 如果做出更改,他们的结果将始终指向他们的版本。
这样,如果课程标准被删除或移动,他们的结果将不会改变。
我目前这样做的方式是处理一个表中的所有数据。 通常我只有一个id字段,但是使用这个系统,我使用了一个id和一个sub_id。 sub_id始终停留在该行,通过更新和删除。 该ID是自动递增的。 课程计划软件将链接到最新的sub_id。 学生的成绩将链接到ID。 我还包括一个时间戳记,用于跟踪发生更改的时间,但不需要处理版本控制。
有一件事我可能会改变,一旦我testing了,我可能会使用前面提到的endDate null的想法。 在我的系统中,要find最新版本,我将不得不find最大(id)。 另一个系统只是查找endDate = null。 不确定是否有其他date字段的好处。
我的两分钱
而@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
,外连接可能会很慢。 好消息是创build新条目在理论上是便宜的,因为你可以在一个写入操作中完成外部事务 (假设你的数据库是primefaces的)。
为'/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} )
我们通过使用旧数据插入。 如果说你只想更新一列并避免乐观locking和/或事务,这是特别有用的。
标记方法和历史表方法需要插入/更新两行。
outer join
修订号方法的另一个优点是,您可以随后使用触发器重构多表方法,因为触发器本质上应该像上面那样做。
只是想补充一点,这个问题的一个很好的解决scheme是使用一个时间数据库 。 许多数据库供应商提供这种function或者开箱即用或通过扩展。 我已经成功地使用了PostgreSQL的时态表扩展,但其他人也有。 无论何时更新数据库中的logging,数据库都会保留该logging的先前版本。