【问题标题】:Concept of "version control" for database table rows (Not referring to storing scripts in GIT/SVN)数据库表行的“版本控制”概念(不是指在GIT/SVN中存储脚本)
【发布时间】:2019-03-07 23:22:42
【问题描述】:

我需要一个数据存储,它不仅可以保存对数据所做更改的历史记录(很容易做到),还可以存储任何数量的对数据提出的更改,包括链式提案(即提案上提案)。

将这些“更改”视为真正长时间运行的事务,这些事务被保存到数据库中,并且具有几分钟到几年的生命周期。

它们被创建(提议),然后回滚(基本上删除)或提交,提交后它们成为对 3 方可见的有效数据。

当然,这一切都需要某种形式的冲突解决,因为提议的更​​改可能处于相互矛盾的状态(例如,更改 A 提议删除记录但更改 B 提议更新它 - 如果首先提交更改 A,那么更改 B 将有恢复)

我发现没有现成的产品可以做到这一点。最接近的是 Oracle Workspace Manager,但它不提供 change-on-change 或查看建议删除的功能。我能够做到这一点的唯一方法是在我的版本化表上拥有一组公共列:

根 ID:必需 - 在创建记录的第一个版本时设置一次与主键相同的值。这代表了所有时间的主键,并被复制到记录的每个版本中。命名关系列时应考虑根 ID(例如 PARENT_ROOT_ID 而不是 PARENT_ID)。由于 Root ID 也是初始版本的主键,因此可以根据实际主键创建外键 - 实际所需的行将由下面定义的版本过滤器确定。

更改 ID:必需 - 每条记录都是通过更改创建、更新、删除的

Copied From ID:Nullable - null 表示新创建的记录,not-null 表示更新/删除时从哪个记录 ID 克隆/分支该行

从日期/时间开始生效:可空 - 空表示建议的记录,非空表示记录何时成为当前记录。不幸的是,不能在 Root ID/Effective From 上放置唯一索引,因为任何 Root ID 都可以有多个空值。 (除非您想将自己限制为每条记录的单个建议更改)

有效到日期/时间:可空 - null 表示当前或建议,not-null 表示何时成为历史。技术上不需要,但有助于加快查找当前数据的查询。此字段可能会因手动编辑而损坏,但如果发生这种情况,可以从生效日期/时间重建。

删除标志:布尔值 - 当建议在成为当前记录时删除记录时设置为 true。提交删除时,它们的生效日期/时间设置为与生效日期/时间相同的值,将它们从当前数据集中过滤掉。

在某个时间点获取数据当前状态的查询将是;

SELECT * FROM table WHERE EFFECTIVE_FROM <= :Now AND (EFFECTIVE_TO IS NULL OR EFFECTIVE_TO > :Now)

根据变化获取数据当前状态的查询是;

SELECT * FROM table WHERE (CHANGE_ID IN :ChangeIds OR (EFFECTIVE_FROM <= :Now AND (EFFECTIVE_TO IS NULL OR EFFECTIVE_TO > :Now) AND ROOT_ID NOT IN (SELECT ROOT_ID FROM table WHERE CHANGE_ID IN :ChangeIds)))

请注意,此第二个查询包含第一个基于时间的查询,以将当前数据与建议的更改数据重叠。

更改 ID 列是指更改表的主键,该表还包含提供更改时更改功能的父 ID 列(可为空)。因此,第二个查询指的是更改 IDs 而不是单个更改 ID。我在客户端的 change-on-change 场景中过滤多个版本,而不使用 SQL,因此在这些查询中看不到它(客户端在内存中有一个更改 ID 的链接列表,如果检索到超过 1 个版本的行它使用链表来确定使用哪个版本)。

有人知道我可以使用的现成产品吗?自己处理这个版本控制需要大量工作,并且会引入各种问题。

【问题讨论】:

  • 你指的是什么数据库? SQL 服务器?甲骨文?
  • 任何 RDBMS 都可以用来伪造这个,如上所述(我已经在 Oracle 中完成了)——我正在寻找的是任何可以执行这种数据版本控制的数据存储盒子。
  • 如果您的编辑回答了您的问题,您应该回滚该编辑并将其作为答案发布。

标签: database database-versioning temporal-database temporal-tables


【解决方案1】:

似乎没有任何现成的数据库或数据库插件可以满足我的需要。所以我最终利用 Oracle 的特性来实现一个解决方案。

最终的表结构略有不同——“删除标志”变成了“更改操作”,即添加、删除或修改。

全局临时表用于存储当前连接更改标识符/日期时间设置,并创建了一个存储过程以在连接后填充它。这被称为“上下文”。

将版本化表连接到这个临时的、特定于连接的上下文表的视图是以编程方式为每个版本化表创建的,包括执行所需数据版本控制的插入/更新/删除触发器。

结果是您将版本化表视为普通表(并且不要将后缀 _ROOT_ID 用于外键)进行选择、插入、更新和删除。

视图中仅返回更改操作,这是区分版本表与普通表的唯一字段。

Revert(没有 SQL 关键字)是通过双重删除实现的。也就是说,如果我们更新一条记录,然后想要撤消该更新,我们发出一个删除命令,该命令删除建议的行并且记录恢复到当前版本。这是最合适的 SQL 关键字 - 替代方法是创建特定的还原存储过程。

视图中存在一个虚拟的无更改操作,表示该记录不受当前上下文的影响。

这一切都非常有效,使版本控制的概念在很大程度上透明,唯一需要的自定义操作是在连接到数据库后设置连接。

【讨论】:

    猜你喜欢
    • 2015-01-11
    • 1970-01-01
    • 2011-01-31
    • 1970-01-01
    • 2021-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多