如何对数据库中的记录进行版本控制[关闭]
Posted
技术标签:
【中文标题】如何对数据库中的记录进行版本控制[关闭]【英文标题】:How to version control a record in a database [closed] 【发布时间】:2010-09-24 07:07:19 【问题描述】:假设我在数据库中有一条记录,管理员和普通用户都可以进行更新。
任何人都可以提出一个好的方法/架构来控制此表中的每个更改,以便可以将记录回滚到以前的版本?
【问题讨论】:
见***.com/questions/125877。 【参考方案1】:作为我上述答案的附加步骤,我建议为每个生成的更改提供一个唯一 ID,可能是日期/时间和每天的唯一计数器(这样一秒钟内的多个更新不会重叠) .我将在此代码中包含一个操作类型代码,即“9129128213939REPLACE”。这提供了一种鲁棒性,允许对您的其他历史记录系统是否正常工作进行健全性检查。
【讨论】:
【参考方案2】:Alok 在上面建议了Audit table
,我想在我的帖子中解释一下。
我在我的项目中采用了这种无模式的单表设计。
架构:
id - 整数自动增量 用户名 - 字符串 表名 - 字符串 旧值 - 文本/JSON 新值 - 文本/JSON createdon - 日期时间此表可以将每个表的历史记录全部保存在一个位置,并且在一条记录中包含完整的对象历史记录。该表可以使用触发器/钩子填充数据更改,存储目标行的新旧值快照。
这种设计的优点:
用于历史管理的表数量更少。 存储每行新旧状态的完整快照。 易于在每个表上进行搜索。 可以按表创建分区。 可以为每个表定义数据保留策略。这种设计的缺点:
如果系统频繁更改,数据量可能会很大。【讨论】:
想出了同样的方法,我相信你不是在这里维护字段,我的意思是基于字段的版本控制,但考虑到一般的记录状态变化【参考方案3】:假设您有一个管理员和用户可以更新的FOO
表。大多数情况下,您可以针对 FOO 表编写查询。快乐的日子。
然后,我将创建一个FOO_HISTORY
表。这包含FOO
表的所有列。主键与 FOO 相同,加上一个 RevisionNumber 列。从FOO_HISTORY
到FOO
有一个外键。您还可以添加与修订相关的列,例如 UserId 和 RevisionDate。在所有 *_HISTORY
表中以不断增加的方式填充修订号(即来自 Oracle 序列或等效的)。不要依赖一秒钟内只有一次更改(即不要将RevisionDate
放入主键中)。
现在,每次更新 FOO
时,就在更新之前将旧值插入 FOO_HISTORY
。您在设计中的某个基本级别上执行此操作,这样程序员就不会意外错过这一步。
如果您想从FOO
中删除一行,您有一些选择。级联并删除所有历史记录,或通过将FOO
标记为已删除来执行逻辑删除。
当您对当前值非常感兴趣并且只是偶尔对历史感兴趣时,此解决方案非常有用。如果您总是需要历史记录,那么您可以输入有效的开始日期和结束日期,并将所有记录保存在 FOO
本身中。然后,每个查询都需要检查这些日期。
【讨论】:
如果您的数据访问层不直接支持,您可以使用数据库触发器更新审计表。此外,构建一个代码生成器来制作使用系统数据字典中的自省的触发器并不难。 我会建议您实际插入 new 数据,而不是之前的数据,这样历史表中就会包含所有数据。虽然它存储冗余数据,但它消除了在需要历史数据时处理两个表中的搜索所需的特殊情况。 我个人建议不要删除任何内容(将其推迟到特定的内务活动)并有一个“操作类型”列来指定它是否是插入/更新/删除。对于删除,您照常复制行,但在操作类型列中输入“删除”。 @Hydrargyrum 保存当前值的表格将比历史表格的视图执行得更好。您可能还想定义引用当前值的外键。There is a foreign key from FOO_HISTORY to FOO'
: 坏主意,我想从 foo 中删除记录而不更改历史记录。历史表在正常使用中应该是只插入的。【参考方案4】:
我认为您正在寻找版本控制数据库记录的内容(就像 *** 在有人编辑问题/答案时所做的那样)。一个好的起点可能是查看一些使用 revision 跟踪的数据库模型。
想到的最好的例子是***引擎 MediaWiki。比较数据库图here,尤其是revision table。
根据您使用的技术,您必须找到一些好的差异/合并算法。
检查 this question 是否适用于 .NET。
【讨论】:
【参考方案5】:只是想补充一点,解决这个问题的一个好方法是使用Temporal database。许多数据库供应商开箱即用或通过扩展提供此功能。我已经成功地将 temporal table 扩展与 PostgreSQL 一起使用,但其他人也有。每当您更新数据库中的记录时,数据库也会保留该记录的先前版本。
【讨论】:
【参考方案6】:@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
)
我们使用旧数据插入。如果您只想更新一列并避免乐观锁定和/或事务,这将特别有用。
标志方法和历史表方法需要插入/更新两行。
outer join
修订号方法的另一个优点是,您以后可以随时使用触发器重构为多表方法,因为您的触发器本质上应该执行与上述类似的操作。
【讨论】:
【参考方案7】:我也在做同样的事情。我正在为课程计划制作一个数据库。这些计划需要原子更改版本控制的灵活性。换句话说,无论多么小,课程计划的每一次更改都需要被允许,但旧版本也需要保持不变。这样,课程创建者可以在学生使用课程计划时对其进行编辑。
它的工作方式是,一旦学生完成课程,他们的结果就会附加到他们完成的版本中。如果进行了更改,他们的结果将始终指向他们的版本。
这样,如果删除或移动课程标准,其结果不会改变。
我目前这样做的方式是在一个表中处理所有数据。通常我只有一个 id 字段,但在这个系统中,我使用了一个 id 和一个 sub_id。 sub_id 通过更新和删除始终与行保持一致。 id 是自动递增的。课程计划软件将链接到最新的 sub_id。学生成绩将链接到 id。我还包含了一个时间戳,用于跟踪更改发生的时间,但不需要处理版本控制。
一旦我测试了它,我可能会改变的一件事是,我可能会使用前面提到的 endDate null 想法。在我的系统中,要找到最新版本,我必须找到 max(id)。另一个系统只是寻找 endDate = null。不确定是否有另一个日期字段的好处。
我的两分钱。
【讨论】:
【参考方案8】:您可以通过 SQL 触发器对 SQL 表执行审计。您可以从触发器访问 2 个特殊表 (inserted and deleted)。这些表包含每次更新表时插入或删除的确切行。在触发器 SQL 中,您可以获取这些修改后的行并将它们插入到审计表中。这种方法意味着您的审计对程序员是透明的;不需要他们的努力或任何实施知识。
这种方法的额外好处是,无论 sql 操作是通过您的数据访问 DLL 还是通过手动 SQL 查询进行的,审计都会发生; (因为审计是在服务器本身上执行的)。
【讨论】:
【参考方案9】:升级到 SQL 2008。
尝试使用 SQL 2008 中的 SQL 更改跟踪。您可以使用此新功能来跟踪数据库中数据的更改,而不是时间戳和墓碑列黑客。
MSDN SQL 2008 Change Tracking
【讨论】:
【参考方案10】:两种选择:
-
拥有一个历史记录表 - 每当更新原始数据时,将旧数据插入此历史记录表。
审核表 - 存储之前和之后的值 - 仅用于审核表中修改后的列以及其他信息,例如更新人员和时间。
【讨论】:
【参考方案11】:你没有说什么数据库,我在帖子标签中也没有看到。如果是针对 Oracle,我可以推荐 Designer 中内置的方法:使用 journal tables。如果是其他数据库,好吧,我基本上也推荐同样的方式......
如果您想在另一个数据库中复制它,或者如果您只是想了解它,它的工作方式是,对于一个表,也创建了一个影子表,只是一个普通的数据库表,带有相同的字段规范,加上一些额外的字段:例如上次执行的操作(字符串,典型值“INS”用于插入,“UPD”用于更新,“DEL”用于删除),操作发生时间的日期时间和用户 ID为谁做的。
通过触发器,对表中任何行的每个操作都会在日志表中插入一个新行,其中包含新值、执行了什么操作、何时执行以及由哪个用户执行。您永远不会删除任何行(至少最近几个月不会)。是的,它会变得很大,很容易达到数百万行,但是您可以轻松地跟踪自日志开始或旧日志行以来任何时间点的 any 记录的值最后一次被清除,以及谁进行了最后一次更改。
在 Oracle 中,您需要的一切都会自动生成为 SQL 代码,您所要做的就是编译/运行它;它带有一个基本的 CRUD 应用程序(实际上只有“R”)来检查它。
【讨论】:
【参考方案12】:在 BI 世界中,您可以通过向要版本化的表添加 startDate 和 endDate 来完成此操作。当您将第一条记录插入表中时,将填充 startDate,但 endDate 为空。插入第二条记录时,还会将第一条记录的 endDate 更新为第二条记录的 startDate。
要查看当前记录时,选择endDate为空的那条。
这有时称为类型 2 Slowly Changing Dimension。 另见TupleVersioning
【讨论】:
使用这种方法我的表不会变得很大吗? 是的,但是您可以通过索引和/或分区表来处理这个问题。此外,只有少数几张大桌子。大多数会小得多。 如果我没记错的话,这里唯一的缺点是它将更改限制为每秒一次,对吗? @pimbrouwers 是的,这最终取决于字段的精度和填充它们的函数。以上是关于如何对数据库中的记录进行版本控制[关闭]的主要内容,如果未能解决你的问题,请参考以下文章