设计辩论:存储和操作版本化对象的好方法是什么?[关闭]
-
08-06-2019 - |
题
一开始我故意让这一点变得非常模糊。我正在寻找讨论以及哪些问题比我正在寻找硬答案更重要。
我正在设计一个应用程序,它可以执行诸如投资组合管理之类的操作。到目前为止我的设计是
- 问题:需要解决的问题
- 解决方案:针对一个或多个问题提出的解决方案
- 关系:两个问题、两个解决方案或一个问题和一个解决方案之间的关系。进一步细分为:
- 父子-某种分类/树层次结构
- 重叠 - 两个解决方案或两个问题真正解决同一概念的程度
- 地址 - 问题解决方案的程度
我的问题是关于这些事情的暂时性。问题突然出现,然后消失。解决方案有一个预期的解决日期,但可能会在开发过程中进行修改。随着问题和解决方案的发展,关系的程度可能会随着时间而改变。
那么,问题是:对这些东西进行版本控制的最佳设计是什么,以便我可以获得我的投资组合的当前和历史视角?
之后:也许我应该提出一个更具体的问题,尽管@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
这只是将关系从问题和解决方案表移动到关系表中。同样的重复问题,但也许有点“干净”,因为我已经有了一个抽象的关系概念。
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
专栏,但这并不 力量 中的正确类型 attributes
桌子。
稍后:回应 @Eric Beard 关于多表外键的评论:
唉,我的描述太简单了:事物只有两种类型(问题和解决方案)。实际上,我有大约 9 或 10 种不同类型的事物,因此根据您的策略,我将有 9 或 10 列外键。我想使用单表继承,但事物的共同点太少了 极其 将它们合并到一张表中是浪费的。
解决方案
嗯,听起来有点像这个网站......
就数据库设计而言,您可能需要一个类似于 SVN 的版本控制系统,您实际上从不进行任何更新,只是在情况发生变化时插入(带有版本号)。这称为 MVCC,多值并发控制。维基百科是另一个很好的例子。
其他提示
@盖乌斯
foreign key (thing_id, thing_type) -> problems.id or solutions.id
小心这些类型的“多向”外键。我的经验表明,当您的联接条件必须在确定要联接的表之前检查类型时,查询性能会受到极大影响。它看起来不那么优雅但可以为空
problem_id and solution_id
会工作得更好。
当然,当您必须添加检查以获取记录的最新版本时,MVCC 设计的查询性能也会受到影响。代价是您永远不必担心更新争用。
您对此有何看法:
表问题
int id |字符串名称|文本描述| DateTime创建的_AT
表问题_修订
int修订| int id |字符串名称|文本描述| DateTime创建的_AT
外键 id -> issues.id
在更新之前,您必须在修订表中执行额外的插入。这个额外的插入速度很快,但是,这是你必须付出的代价
- 高效访问当前版本 - 像往常一样选择问题
- 直观且接近您想要建模的现实的模式
- 架构中的表之间的连接保持高效
- 使用每个业务事务的修订号,您可以对表记录进行版本控制,就像 SVN 对文件所做的那样。
我想有
选项 4:混合体
将通用事物属性移至单继承表中,然后添加 custom_attributes
桌子。这使得外键更简单,减少重复,并提供灵活性。它没有解决附加属性的类型安全问题。它还增加了一点复杂性,因为现在有两种方法可以让事物拥有属性。
如果 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
最好选择一种数据结构,使您向模型提出的常见问题易于回答。您很可能大部分时间都对当前职位感兴趣。有时,您会想要深入了解特定问题和解决方案的历史。
我会有代表当前位置的问题、解决方案和关系的表格。还会有一个 problem_history
, solution_history
, 等表。这些将是问题的子表,但也包含额外的列 VersionNumber
和 EffectiveDate
. 。关键是(ProblemId
, VersionNumber
).
当您更新问题时,您会将旧值写入 problem_history
桌子。因此,时间点查询是可能的,因为您可以选择 problem_history
在特定日期有效的记录。
在我之前完成此操作的地方,我还创建了 UNION 视图 problem
和 problem_history
因为这有时在各种查询中很有用。
选项 1 使得查询当前情况变得困难,因为所有历史数据都与当前数据混合在一起。
选项 3 对查询性能不利,并且难以编写代码,因为您将访问大量行来执行一个简单的查询。