数据库版本控制

时间:2009-12-07 05:23:20

标签: database database-design version

我做了很少的项目(CMS和EC系统),需要对某些数据进行版本控制。

通常我会带来那种架构

+--------------+
+ foobar       +
+--------------+
+ foobar_id    +
+ version      +
+--------------+

它工作得很好,但我想知道是否有更好的办法。该解决方案的主要问题是您必须始终使用子查询来获取最新版本。

即:

SELECT * FROM foobar WHERE foobar_id = 2 and version = (SELECT MAX(version) FROM foobar f2 WHERE f2 = 2)

这使得大多数查询更复杂,并且还存在一些性能缺陷。

如果您分享创建版本化表格的经验以及每种方法的优点和缺点,那将会很好。

由于

7 个答案:

答案 0 :(得分:5)

我更喜欢将历史数据放在另一个表中。我会制作foobar_history或类似的东西,并制作一个FK到foobar_id。这将阻止您必须一起使用子查询。这样做的另一个好处是不会使用大量历史数据污染您的主数据表,您可能不希望在99%的时间内访问它。

您可能希望触发更新此数据,因为它需要您将当前数据复制到_history然后进行更新。

答案 1 :(得分:2)

我认为最干净的解决方案是为每个需要版本化的表创建一个History表。换句话说,有一个foobar表,然后是foobar_History表,在foobar上有一个触发器,它将使用时间戳和改变数据的用户将现有数据写入History表。较旧的数据很容易查询,按时间戳降序排序,您知道主表中的数据始终是最新版本。

答案 2 :(得分:2)

我曾经在一个有历史数据的系统上工作,我们有一个布尔值来指示哪一个是最新版本的数据。当然,你需要在适用级别保持旗帜的一致性。然后,您可以创建使用该标志的索引,如果您在where子句中提供它,则它很快。

临:

  • 易于理解
  • 不需要对(现有)数据库架构进行重大更改
  • 无需复制另一个表中的旧数据,只更新标志。

缺点:

  • 标志需要维持在适用级别

否则,您可以依赖单独的历史记录表,如多个答案所示。

临:

  • 从实际数据中清除历史记录
  • 可能在实际数据及其历史记录之间删除数据库级别级别,以防实体被删除

缺点:

  • 如果您想要完整的历史记录(即旧数据+当前数据),则需要2个查询(或联合)
  • 将更新与最新版本数据对应的行。我听说更新比插入慢,这取决于更改数据的“大小”。

最好的将取决于您的用例。我不得不处理一个文档管理系统,我们希望能够对文档进行版本控制。但我们也有恢复到旧版本的功能。使用布尔值更容易加速只需要最后一个的操作。如果您有真实的历史数据(永远不会改变),那么专用历史表可能更好。

历史概念是否适合您的域模型?如果不是,那么您的数据库模式与概念域模型不同。如果在域级别,实际数据和旧数据需要以相同的方式处理,有两个表使设计复杂化。只需考虑您需要返回完整历史记录的案例(旧+新)。最简单的解决方案是为每个表创建一个类,但是您不能像只有一个表那样容易地返回列表。但如果这些是两个截然不同的概念,那么将历史作为设计中的第一类是很好的。

我还建议M. Fowler在处理时态数据时也很有趣:Patterns for things that change with time

答案 3 :(得分:1)

您可以使用表格上的视图来过滤到最新版本,从而简化查询。这只会使查询看起来更好,但仍然会有性能开销。

答案 4 :(得分:1)

常用技术是为当前/已过期添加列version_status。另外需要注意的是,如果您在同一个表中保留新旧记录,则应该为您的实体设置业务(自然)密钥,例如name + pin,因为主键将随每行更改(递增)。

TABLE foobar(foobar_id PK, business_key, version, version_status, .....)

SELECT * 
FROM foobar 
WHERE business_key = 'myFoobar3' AND version_status = 'current'

当决定将记录历史记录保存在同一个表中 - 或将其移动到单独的表中时 - 请考虑将foobar_id作为外键的其他表。发布新版本时,现有外键是应该指向新PK还是旧PK?如果您想保留关系历史记录,您可能希望将所有内容保存在同一个表中。如果只有新版本很重要,您可以考虑将过期的行移动到另一个表 - 尽管没有必要。

答案 5 :(得分:0)

如果您使用过Oracle,则可以使用分析函数

选择*来自( 选择一个。* ,row_number()over(按版本desc按foobar_id顺序分区)rn 来自foobar a 在哪里foobar_id = 2 )其中rn = 1

答案 6 :(得分:0)

这取决于您的表中有多少需要版本控制,以及您是否有事务矿石报告系统。

如果只是几个事务表 - 只要性能问题不是太大,你正在做的事情就好了。您可以通过为current_row添加一列以及更新前一行以使其成为非当前行的触发器来简化查询。

但是如果你有很多表,或者额外的行减慢了你的一些查询,那么我会像其他人一样建议并使用历史表和历史触发器。请注意,您可以生成该代码,以便更容易开发和维护。

如果您处于报告世界,那么我还有很多其他选项,我不会在此处讨论。您可以在数据仓库数据建模书籍中找到详细选项。