实现数据库对象的版本控制

Posted

技术标签:

【中文标题】实现数据库对象的版本控制【英文标题】:Implementing Version Control of DB Objects 【发布时间】:2010-11-20 02:30:28 【问题描述】:

我将很快开始一个项目的工作,该项目(根据规范)让我想起了 ***。基本上,它是一个具有用户控制内容的网络应用程序。

让我在脑海中转来转去的功能之一是版本控制。在 *** 上,每个问题和答案都可以有多个修订。当您只有一种类型的对象(在本例中是它的文本)时,这很容易实现。

所以,对于我的简单页面,我准备好了。

当我认为某些需要受版本控制的对象具有关系时,问题就出现了。为了提供一个具体的例子,让我选择一个随机的类比域:

假设我正在实施一个类似于 Wiki 的网站来跟踪书籍/作者信息。 该站点的主要重点是创建和更新“作者”页面,作为文本,这些页面非常简单(如上所述)。然而,让我们在作者和书籍之间添加一个一对多的关联(换句话说,书籍将是单独的对象,因为显然一个人可以编写许多书籍)。每本书都有一个从作者页面到关于该书的信息页面的链接。

对于用户而言,描述作者的基于文本的“摘要”与该作者与其作品之间的链接几乎没有区别。因此,我们需要为作者页面、书籍页面以及作者与书籍之间的关联实现“修订”/编辑功能。换句话说,用户应该能够编辑、查看历史记录和回滚作者页面、书籍页面以及两者之间的关联。

当这种关系变成多对多时,情况会变得更加复杂,其中可以将多个作者列为对一本书的贡献。

我想到了许多解决方案,但没有一个像我想要的那样干净(并且至少涉及一些重复的代码/冗余数据存储),尽管我确实在这里看到了共同点,我觉得我还没有真正能够最好地提取它,尤其是在数据库级别。我不想对给出的答案产生偏见,所以我不会马上给出。

那么,您将如何在数据库级别设计此系统? 我正在这里寻找表规范,如果不是很明显的话,可能还有关于如何使用它们的描述。对于那些可能相关的答案,我将使用 ASP.NET 和 Linq-to-SQL(我对 LTS 中的多对多感到满意)或实体框架。

编辑:澄清一下,我了解基本的数据库设计、规范化、多对多映射表等。我正在为这种特定情况寻找一个干净的解决方案。

编辑 2: 我正在寻找一个通用的解决方案,因为系统中可能有更多的子对象,而不仅仅是书籍。作者可能与其他作者、杂志、事件等有关。如果我为每个作者单独实现历史,我觉得我正在重复很多工作。

【问题讨论】:

@JoshJordan:不要道歉。修正问题,使其更切中要害。 【参考方案1】:

听起来几乎是 CouchDB 的理想用例。使用这个面向文档的数据库,您可以免费获得修订(每个文档都会自动修订,除非您配置不同的数据库)。

文档之间也可以有 m:n 关系。但是,迁移到 CouchDB 是相当大的一步,我不知道从 ASP.NET 访问它的效果如何。但是阅读一些介绍性教程不会有什么坏处。

【讨论】:

【参考方案2】:

这是数据仓库中常见的问题。他们使用“缓慢变化的维度”。

但是,如果您要尝试拥有“版本化”数据,则必须有一些规则。

    您必须按照最初定义的方式记录作者-图书关系。这是官方的作者-书籍关系。这是数据仓库人员所说的“无事实事实表”。这是一对钥匙。

    书籍是书籍作者事实的一个维度。书可以改。有许多缓慢变化的维度算法。您只能保留最新的,将历史表与当前分开。将历史和当前保存在一个表中,并带有一个标志以区分当前和历史。

    作者是书籍作者事实的一个维度。作者可以改。同样,有许多 SCD 算法。继续阅读选择。作者:Ralph Kimball 的数据仓库工具包了解更多信息。

请注意,关系(作者与书籍)是事实,不需要版本。这是事实。它不会“改变”。它要么是真的,要么是错误地放入数据库中——在这种情况下,它必须被删除。事实不需要版本号。

在更复杂的星型模式中,您的事实有衡量标准。价格、销售量、成本、利润等。这些也记录在事实表中。这些信息可能会随时间而变化。因此,您几乎总是对每个事实都有一个时间维度。

因此,时间是书籍作者事实的一个维度。如果这一事实可能发生变化,则将适用的时间段记录为事实的一部分。

时间维度与版本号并不完全相同。它稍微简单一些。它指出,在给定的时间点,事实是真实的。如果事实发生变化,您将附加一个具有不同时间戳的新事实。

您可以在特定时间点找到相关事实和相关维度值。

【讨论】:

谢谢。我应该重新考虑为什么/是否要为每个表创建一个单独的历史记录表,而不是将每个表的旧数据保存在表本身中。 @ChrisW:SCD 设计很难。这取决于您将获得的查询类型。人们会做“反事实”(“假设”)查询吗? “如果这些销售数字是按照去年的地区定义报告的呢?”在这种情况下,您可能会加入历史维度行。如果您很少这样做,单独的历史记录表不会受到影响。如果您经常这样做,单独的历史记录表可能会过于复杂。 一件事是,在同一个表中拥有历史数据会使实现参照完整性变得更加困难(或不那么直观)。例如,可能要求每本书都有一个相应的作者。通常,您可以使用外键执行此操作。但是,如果作者表包含已删除的作者,则外键不是足够的约束(因为它允许您插入引用已删除作者的新书)。 我猜你必须将它定义为(更复杂的)CHECK 约束,而不是简单的外键,它应该检查具有给定 AuthorId 和所需 AuthorStatus 值的记录.【参考方案3】:

每个表都有一个表:即 Author 和 Book。

表之间存在通常的外键关系(无论是什么)。

每个表还有一个历史表,即 AuthorHistory 和 BookHistory。这些历史表包含记录的旧版本/过时版本(例如,每个已删除和/或编辑的作者记录)。与历史记录表之间没有外键关系。


编辑:

每个表的某些功能是相似的:例如,无论是哪个表,更新记录都意味着将记录的旧副本存储在相应的历史表中。我使用数据库触发器(每个表的更新和删除触发器)来实现此功能;因为我使用的数据库引擎支持触发器,这使得它对应用程序透明。这些触发器中的代码从一个表到下一个表是相似的(只是表的名称和字段名称列表因表而异)。


多对多的情况呢?这更加困难,因为您实际上可能没有将作者映射到一本书的记录,但以前有一个并且需要将其显示为历史项目

编辑#2:

我还没有实现多对多情况的历史记录,但我不明白为什么它会不一样,即:

多对多关系是通过一个 BookAuthor 表实现的,每个表的记录就是 BookId 加上 AuthorId。 历史关系位于对应的 BookAuthorHistory 表中。

【讨论】:

多对多的情况呢?这更加困难,因为您实际上可能没有将作者映射到一本书的记录,但以前有一个并且需要将其显示为历史项目。 确实,你是对的。不幸的是,这不是一个非常通用/可扩展的解决方案。它需要为每个实现的新表创建一个新的历史表。 我看不出有什么不通用/可扩展的:IMO 这是一个“通用”解决方案,因为它适用于任何一组表。 从设计的角度来看是通用的,我正在寻找通用的实现。 我不明白你的意思。无论如何,在频谱的另一端有一个不同的解决方案是拥有一个历史表,其中包含数据库中每个其他表的所有历史字段值:en.wikipedia.org/wiki/Entity-attribute-value_model(我已经看到它说使用 EAV 是许多程序员在职业生涯中的某个时候犯的错误)。

以上是关于实现数据库对象的版本控制的主要内容,如果未能解决你的问题,请参考以下文章

代码的自动语义版本控制

腌制对象版本控制

MySQL 查询版本控制表中存在多个版本记录如何实现只取最大版本号对应记录

VB 数据库操作实例:写入软件版本号(版本控制代码实现)

在哪里可以找到有关 Core Data 对象模型版本控制和迁移的更多详细信息?

程序开发数据库版本控制必备 - Flyway