0

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

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

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

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

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

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

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

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

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

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

删除标志:布尔值 - 当建议在成为当前记录时删除记录时设置为 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 列(可为空)。因此,第二个查询指的是更改 ID 而不是单个更改 ID。我在客户端的 change-on-change 场景中过滤多个版本,而不使用 SQL,因此在这些查询中看不到它(客户端在内存中有一个更改 ID 的链接列表,如果检索到超过 1 个版本的行它使用链表来确定使用哪个版本)。

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

4

1 回答 1

1

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

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

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

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

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

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

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

视图中存在“无”的虚拟更改操作,这表明记录不受当前上下文的影响。

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

于 2019-10-17T20:03:42.633 回答