【问题标题】:Database Modelling - Conceptually different entities with near identical fields数据库建模 - 具有几乎相同字段的概念上不同的实体
【发布时间】:2011-01-29 07:27:41
【问题描述】:
假设您有两组概念实体:
-
MarketPriceDataSet 有多个 ForwardPriceEntries
-
PoolPriceForecastDataSet 有多个 PoolPriceForecastEntry
两个不同的子对象具有几乎相同的字段:
ForwardPriceEntry 有
- 开始日期
- 结束日期
- SimulationItemId
- 远期价格
- MarketPriceDataSetId(父表的外键)
PoolPriceForecastEntry 有
- 开始日期
- 结束日期
- SimulationItemId
- 预测池价格
- PoolPriceForecastDataSetId(父表的外键)
如果我将它们建模为单独的表,唯一的区别就是外键和价格字段的名称。
关于两个几乎相同的表是否应该合并为一个的问题一直存在争议。
我想到的建模选项是:
- 只需将它们保留为两个独立的单独表格
- 将两个集合放在一个表中,并带有一个额外的“类型”字段,以及一个与任一父表的外键相等的 parent_id。这会牺牲参照完整性检查。
- 将这两个集合放在一个带有附加“类型”字段的表中,并创建一个复杂的连接表序列以保持参照完整性。
你认为我应该怎么做,为什么?
其他可能相关或不相关的信息:
【问题讨论】:
标签:
database-design
relational-database
【解决方案1】:
“关于是否应该将两个几乎相同的表合并为一个的问题一直存在争议。”
有吗?
在数据库设计中,如果谓词不同,表应该保持独立。也就是说,如果表中各行所附加的含义不同。列集是否几乎或什至完全相等,无关紧要。
【解决方案2】:
在您描述的情况下,被建模的主题通常符合一种称为“泛化专业化”的模式。两个先前的答案讨论子类型的事实支持了这一点。对象模型倾向于通过使用子类型和继承来处理 gen-spec。
关系模型也可以处理 gen-spec,但它稍微复杂一些,并且通常不会在数据库入门中教授。你要做的是有几个表,每个子类型一个,保存子类型特有的键和非键属性。与超类型相关并由所有子类型继承的数据通常合并到一个表中。
您可以使用这些表的键来玩一个技巧,以减少存储和处理。在子类型表中,使用返回超类型条目的外键作为子类型表的主键。它保证存在,并且保证是唯一的。何必再发明一把钥匙呢?
如果您搜索“泛化专业化关系建模”,您将获得关于该主题的十几篇文章。其中一些非常好。也可以关注标签:class-table-inheritance
【解决方案3】:
由于实体共享如此多的属性,您可以将它们视为子类型。
从逻辑上讲,您将拥有 PriceEntry 的超类型以及 ForwardPriceEntry 和 PoolPriceForecastEntry 的子类型。要回答的一个问题是价格对于超类型是否通用。我会假设它是。
现在的问题是如何物理实现子类型。您可以采取 3 种方法:
- 为每个子类型创建一个表(Rolldown)
- 创建包含所有属性的单个表(汇总)
- 为每个子类型(身份)创建一个超类型表和一个子类型表
这些方法中的每一种都有利有弊。有关每种方法的优缺点的讨论,请参阅Getting Physical with Subtypes。
在这种情况下,由于子类型共享许多属性,您可以采用汇总方法并创建一个表。
PriceEntry 表可能如下所示:
PriceEntryId (PK)
PriceEntryTypeCode (NN)
StartDate (NN)
EndDate
SimulationItemId (NN)
Price (NN)
MarketPriceDataSetId (FK)
PoolPriceForecastDataSetId (FK)
您仍然可以对 MarketPrice 和 PoolForecast 列强制执行 FK。您还可以添加表级检查约束以确保至少填充一个 FK。
但是,由于 2 个子类型之间只有一个属性不同,因此许多优点和缺点并没有强烈地指向一个方向。所以,最后,我可能会选择让数据模型易于理解和使用。对我来说,两张表(rolldown)方法取得了很好的平衡;从概念上讲,它比汇总更容易理解,并且在开发时它比标识更容易使用(没有连接或多次插入/更新)。
【解决方案4】:
如果您打算以大致相同的方式使用来自两个子类型的数据,我建议将所有数据放入包含所有五个字段的单个表中...
- 开始日期
- 结束日期
- SimulationItemId
- 远期价格
- MarketPriceDataSetId(父表的外键)
... 并在 ForwardPrice 或 ForecastPoolPricedepending 字段中放置一个空值,以等待特定行支持的父实体。每行空值的空间成本几乎不存在,压缩成功率在表上会非常高,但对于索引和读取,您的性能会令人印象深刻。