数据库设计:我有一个 child table 可能被多个 parent table (one-to-one) 使用。我如何设计它的架构
Database Design: I have a child table which might be used from more than one parent table (one-to-one). How do I design its schema
假设我有一个名为 offers
的 child table。 offers
可以从多个 table 引用,例如 purchase
、membership
等。请注意,将来可能会有更多 table引用自.
购买和会员资格已经存在于数据库中,并且它们将始终在相应的优惠 object 之前创建。把它想象成购买 X 可能会提供报价 Y(一对一)
为了在关系模式中实现这一点,我想到了两个可能的选择。
选项 1
offers
table 不会有任何引用。相反,purchase
table 和 membership
table 将有一个名为 offer_id
的列,它引用 offer
table。如下所示
table offers {
id,
offer_name,
}
table purchase {
id,
..other fields,
offer_id fk(offers),
}
table membership {
id,
..other fields,
offer_id fk(offers),
}
选项 2
offers
table 将包含一个 type
字段,它指的是要约的类型(purchase
、membership
等),并且会有是引用每个 table.
的多个可空外键
table offers {
id,
offer_name,
type enum(purchase, membership,
purchase_id fk(purchases) NULL,
membership_id fk(memberships) NULL,
}
table purchase {
id,
..other fields,
}
table membership {
id,
..other fields,
}
我觉得方案一是最简单的方法,也是正确的方法。但是感觉有违数据库设计原则(Parent不应该引用child,等等)。
执行此操作的正确方法应该是什么?如果我选择选项 1,会出现什么问题?
由于您在 purchase
或 membership
实体 之后创建了 offer
实体,因此 offer
是 child 实体,因此应该有 parent table.
的外键
我知道相反的做法很诱人,但语义在数据库设计中很重要:
- 如果您的 FK 在 parent 个实体中,那么您 运行 有不属于任何实体的孤立报价的风险。您不能通过 FK 强制完整性。
- 您无法通过标准数据库技术阻止将 1 个报价分配给多个实体。您需要编写额外的应用程序代码来执行此规则。
- 任何查看您的 ERD 的人都会对关系的性质得出错误的初步结论,因此您必须向每位新同事提供额外的解释。
- 如果您在数据库架构之上使用 ORM,ORM 将再次错误地解释关系,在 FK 之后设置 类 和关系。同样,需要额外的解释。
您有三个选项来设计 offer
table 中的外键以遵循选项 2:
- 每个 parent 都有一个单独的 FK 字段。优点是很清楚 child 属于哪个 parent 实体,并且很容易强制执行参照完整性。缺点是您需要创建与 parent 个实体一样多的字段,并且需要更改 table 才能添加新字段。如果您的 parent 个实体数量有限且数量为 stable,效果很好。
- 使用
type
和 parent_id
字段组合。类型字段告诉您记录属于哪个parent。这与之前的解决方案相反 - 可以灵活地添加新的 parent 类型,但是通过数据库工具强制执行参照完整性并不简单!
- 为每种 parent 类型使用单独的商品 table。如果您计划按类型区分各种优惠,这种设计会很有用。
假设我有一个名为 offers
的 child table。 offers
可以从多个 table 引用,例如 purchase
、membership
等。请注意,将来可能会有更多 table引用自.
购买和会员资格已经存在于数据库中,并且它们将始终在相应的优惠 object 之前创建。把它想象成购买 X 可能会提供报价 Y(一对一)
为了在关系模式中实现这一点,我想到了两个可能的选择。
选项 1
offers
table 不会有任何引用。相反,purchase
table 和 membership
table 将有一个名为 offer_id
的列,它引用 offer
table。如下所示
table offers {
id,
offer_name,
}
table purchase {
id,
..other fields,
offer_id fk(offers),
}
table membership {
id,
..other fields,
offer_id fk(offers),
}
选项 2
offers
table 将包含一个 type
字段,它指的是要约的类型(purchase
、membership
等),并且会有是引用每个 table.
table offers {
id,
offer_name,
type enum(purchase, membership,
purchase_id fk(purchases) NULL,
membership_id fk(memberships) NULL,
}
table purchase {
id,
..other fields,
}
table membership {
id,
..other fields,
}
我觉得方案一是最简单的方法,也是正确的方法。但是感觉有违数据库设计原则(Parent不应该引用child,等等)。
执行此操作的正确方法应该是什么?如果我选择选项 1,会出现什么问题?
由于您在 purchase
或 membership
实体 之后创建了 offer
实体,因此 offer
是 child 实体,因此应该有 parent table.
我知道相反的做法很诱人,但语义在数据库设计中很重要:
- 如果您的 FK 在 parent 个实体中,那么您 运行 有不属于任何实体的孤立报价的风险。您不能通过 FK 强制完整性。
- 您无法通过标准数据库技术阻止将 1 个报价分配给多个实体。您需要编写额外的应用程序代码来执行此规则。
- 任何查看您的 ERD 的人都会对关系的性质得出错误的初步结论,因此您必须向每位新同事提供额外的解释。
- 如果您在数据库架构之上使用 ORM,ORM 将再次错误地解释关系,在 FK 之后设置 类 和关系。同样,需要额外的解释。
您有三个选项来设计 offer
table 中的外键以遵循选项 2:
- 每个 parent 都有一个单独的 FK 字段。优点是很清楚 child 属于哪个 parent 实体,并且很容易强制执行参照完整性。缺点是您需要创建与 parent 个实体一样多的字段,并且需要更改 table 才能添加新字段。如果您的 parent 个实体数量有限且数量为 stable,效果很好。
- 使用
type
和parent_id
字段组合。类型字段告诉您记录属于哪个parent。这与之前的解决方案相反 - 可以灵活地添加新的 parent 类型,但是通过数据库工具强制执行参照完整性并不简单! - 为每种 parent 类型使用单独的商品 table。如果您计划按类型区分各种优惠,这种设计会很有用。