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

时间:2020-03-05 18:39:45  来源:igfitidea点击:

首先,我故意让这个含糊不清。我正在寻找讨论,哪些问题比寻找困难的答案更重要。

我正在设计一个应用程序,该应用程序执行投资组合管理之类的工作。我到目前为止的设计是

  • 问题:需要解决的问题
  • 解决方案:针对一个或者多个问题的建议解决方案
  • 重叠-两个解决方案或者两个问题真正解决同一概念的程度
  • 解决-问题解决解决方案的程度

我的问题是关于这些事物的时间性质。问题浮出水面,然后消失。解决方案具有预期的解决日期,但是在开发过程中可能会进行修改。随着问题和解决方案的发展,关系的程度可能会随着时间而改变。

所以,问题是:什么是对这些东西进行版本控制的最佳设计,这样我才能既了解当前投资组合又了解历史投资组合?

稍后:也许我应该提出一个更具体的问题,尽管@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

这只是将关系从"问题和解决方案"表移到"关系"表中。同样的复制问题,但是可能有点"清洁",因为我已经有了一个抽象的Relationship概念。

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

这意味着要加载问题或者解决方案的当前版本,我必须获取该属性的所有版本,按日期对其进行排序,然后使用最新版本。那可能并不可怕。对我而言似乎真正糟糕的是,我无法在数据库中对这些属性进行类型检查。 "值"列必须是自由文本。我可以将"名称"列作为对具有"类型"列的单独"属性_名称"表的引用,但这不会在"属性"表中强制使用正确的类型。

稍后:响应@Eric Beard关于多表外键的评论:

las,我所描述的很简单:事物只有两种类型(问题和解决方案)。实际上,我大约有9或者10种不同类型的事物,因此根据策略,我将有9或者10列外键。我想使用单表继承,但是事物之间的共同点太少了,以至于将它们组合成一个表是非常浪费的。

解决方案

回答

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

就数据库设计而言,像SVN这样的版本控制系统实际上并不需要进行任何更新,而可能在我们需要更改时插入(带有版本号)。这称为MVCC,多值并发控制。维基是另一个很好的例子。

回答

@盖乌斯

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

请小心使用这些"多向"外键。我的经验表明,当联接条件在确定要联接的表之前必须检查类型时,查询性能会遭受严重影响。它看起来并不优雅,但可以为空

problem_id and solution_id

会更好地工作。

当然,当我们必须添加检查以获取记录的最新版本时,MVCC设计也会影响查询性能。折衷方案是我们不必担心更新争用。

回答

我想有

选项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

回答

我们如何看待这一点:

表问题
整数ID |字符串名称|文字说明|日期时间created_at

表问题_修订版
内部修订|整数ID |字符串名称|文字说明|日期时间created_at
外键ID-> Problems.id

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

  • 有效访问当前版本-像往常一样选择问题
  • 直观且接近我们要建模的现实的架构
  • 模式中表之间的连接保持有效
  • 使用每个业务事务的修订号,我们可以对表记录进行版本控制,就像SVN对文件进行版本控制一样。

回答

选择一个数据结构是一个好主意,该数据结构使我们对模型提出的常见问题易于回答。我们很可能大部分时间都对当前职位感兴趣。有时,我们会希望深入了解特定问题和解决方案的历史记录。

我将有代表当前职位的问题,解决方案和关系表。还有一个" problem_history"," solution_history"等表。这些将是有问题的子表,但还包含" VersionNumber"和" EffectiveDate"的额外列。密钥将是(ProblemIdVersionNumber)。

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

在此之前,我还创建了UNION问题和问题历史视图,因为有时在各种查询中它很有用。

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

选项3不利于查询性能,不利于进行代码编写,因为我们将访问许多行,而这些行应该只是一个简单的查询。