设计辩论:存储和操作版本化对象的好方法是什么?

时间:2008-08-14 20:50:12

标签: architecture time rdbms versions

我一开始故意把这个模糊不清。我正在寻找讨论,哪些问题比我正在寻找难的答案更重要。

我正在设计一个类似投资组合管理的应用程序。我到目前为止的设计是

  • 问题:需要解决的问题
  • 解决方案:针对一个或多个问题的建议解决方案
  • 关系:两个问题,两个解决方案或问题和解决方案之间的关系。进一步细分为:
    • 亲子 - 某种分类/树层次结构
    • 重叠 - 两个解决方案或两个问题真正解决相同概念的程度
    • 地址 - 问题解决方案的程度

我的问题是关于这些事情的时间性质。出现问题,然后褪色。解决方案具有预期的解决方案日期,但可能会在开发时进行修改。随着问题和解决方案的发展,关系的程度可能会随着时间而变化。

所以,问题是:对这些东西进行版本控制的最佳设计是什么,这样我就可以获得我的投资组合的当前和历史视角?

后来:也许我应该把它作为一个更具体的问题,尽管@Eric Beard的答案值得一试。

我考虑了三种数据库设计。我足以证明他们的缺点。我的问题是:选哪个,或者你能想出更好的东西吗?

1:问题(和单独的解决方案)在版本控制中是自引用的。

table problems
  int id | string name | text description | datetime created_at | int previous_version_id

  foreign key previous_version_id -> problems.id

这是有问题的,因为每次我想要新版本时,我都必须复制整行,包括那个长description列。

2:创建新的关系类型:版本。

table problems
  int id | string name | text description | datetime created_at

这只是将Problems和Solutions表中的关系移动到Relationships表中。同样的重复问题,但也许有点“清洁”,因为我已经有了一个抽象的关系概念。

3:使用更像Subversion的结构;将所有问题和解决方案属性移动到单独的表中并对其进行版本化。

table problems
  int id

table attributes
  int id | int thing_id | string thing_type | string name | string value | datetime created_at | int previous_version_id

  foreign key (thing_id, thing_type) -> problems.id or solutions.id
  foreign key previous_version_id -> attributes.id

这意味着要加载问题或解决方案的当前版本,我必须获取属性的所有版本,按日期对它们进行排序,然后使用最新版本。那可能并不可怕。对我来说真正糟糕的是我无法在数据库中键入 - 检查这些属性。 value列必须是自由文本。我可以将name列引用到具有attribute_names列的单独type表中,但不会强制 { {1}}表。

后来仍然:回应@Eric Beard关于多表外键的评论:

唉,我所描述的是简单化的:只有两种类型的东西(问题和解决方案)。我实际上有大约9或10种不同类型的东西,所以我的策略下有9或10列外键。我想使用单表继承,但事情几乎没有什么共同之处,将它们组合到一个表中是非常浪费。

5 个答案:

答案 0 :(得分:1)

嗯,听起来有点像这个网站......

就数据库设计而言,版本系统类似于SVN,你实际上从未进行任何更新,只需在事情发生变化时插入(带有版本号),可能就是你所需要的。这称为MVCC,多值并发控制。维基是另一个很好的例子。

答案 1 :(得分:1)

@Gaius

foreign key (thing_id, thing_type) -> problems.id or solutions.id

小心这些“多向”外键。我的经验表明,当你的连接条件必须在确定要加入哪个表之前检查类型时,查询性能会受到严重影响。它似乎不是优雅但可以为空的

problem_id and solution_id 

会更好。

当然,当您必须添加检查以获取最新版本的记录时,查询性能也会受到MVCC设计的影响。权衡是你永远不必担心与更新争用。

答案 2 :(得分:1)

您如何看待这个:

表问题
  int id |字符串名称|文字说明| datetime created_at

表格问题   int revision | int id |字符串名称|文字说明| datetime created_at
  外键id - > problems.id

在更新之前,您必须在修订表中执行其他插入。这个额外的插入很快,但是,这是你需要支付的费用

  1. 有效访问当前版本 - 照常选择问题
  2. 一种直观且接近您想要建模的现实的架构
  3. 在架构中的表之间连接保持高效
  4. 使用每个业务交易的修订号,您可以对表格记录进行版本控制,例如SVN对文件进行版本控制。

答案 3 :(得分:0)

我想有

选项4:混合

将常见Thing属性移动到单继承表中,然后添加custom_attributes表。这使得外键更简单,减少重复,并允许灵活性。它没有解决附加属性的类型安全问题。它还增加了一点复杂性,因为Thing现在有两种方法可以拥有一个属性。

如果description和其他大字段保留在Things表中,它也无法解决重复空间问题。

table things
  int id | int type | string name | text description | datetime created_at | other common fields...
  foreign key type -> thing_types.id

table custom_attributes
  int id | int thing_id | string name | string value
  foreign key thing_id -> things.id

答案 4 :(得分:0)

选择一个数据结构是个好主意,这个数据结构可以让你对模型提出的常见问题很容易回答。你最有可能在大多数时候对目前的职位感兴趣。有时,您需要深入了解特定问题和解决方案的历史记录。

我会为表示当前位置的问题,解决方案和关系提供表格。还会有一个problem_historysolution_history等表格。这些是问题的子表,但也包含VersionNumberEffectiveDate的额外列。关键是(ProblemIdVersionNumber)。

更新问题时,您可以将旧值写入problem_history表。因此,您可以选择时间点查询,因为您可以选择在特定日期有效的problem_history记录。

之前我已经完成了这项工作,我还创建了一个UNION problemproblem_history的视图,因为这在各种查询中有时很有用。

选项1使查询当前情况变得困难,因为所有历史数据都与您当前的数据混合在一起。

选项3对于查询性能会很糟糕,而且对于代码来说是讨厌的,因为您将访问许多行,这应该只是一个简单的查询。