多态关系与每种类型的单独表
Polymorphic relationships vs separate tables per type
我正在处理具有某些类型(例如 User
、Appointment
、Task
等)的数据库,这些类型可以有零个或多个 Notes
关联每种类型。
我遇到的实现这些关系的可能解决方案是:
- 多态关系
- 每种类型table
多态关系
许多人认为这是最容易实现的解决方案,而且似乎是遵循 Active Record 模式的框架最常见的实现,我会添加一个 table,其数据是 morphable
:
我的 notable_type
可以让我区分 Note
相关的类型(User
、Appointment
、Task
),而 notable_id
将允许我在相关 type
table.
中获取个人 type
记录
优点:
- 易于扩展,更多的模型可以很容易地与多态关联class
- 限制table膨胀
- 一个 class 的结果可以被许多其他 classes 使用(DRY)
缺点
- 随着数据的增长,更多类型会使查询变得更加困难和昂贵
- 不能有外键
- 缺乏数据一致性
每种类型Table
或者,我可以为每种类型创建一个 table,它只负责与该类型关联的 Notes
。 type_id
外键可以让我快速获取个人 type
记录。
网上很多人认为这是一种代码味道,许多文章提倡避免多态关系,转而采用替代关系(例如here and here)。
优点:
- 允许我们有效地使用外键
- 高效的数据查询
- 保持数据一致性
缺点:
- 增加 table 膨胀,因为每种类型都需要单独的 table
- 结果为多个 class,每个代表单独的
type_notes
table
想法
多态关系当然是两个选项中更容易实现的一个,但是缺少外键约束并因此可能出现一致性问题让人感觉不对。
A table per notes
relationship (user_notes
, task_notes
etc.) with foreign keys seems the correct way (in keeping with design patterns) but could result在许多 table 中(添加其他可以具有 notes
的 types
或添加类似于 notes
的类型 [例如 events
])。
感觉我的选择要么简化table结构但放弃外键并增加查询开销,要么增加具有相同结构但简化查询并允许外键的table的数量键。
考虑到我的情况,以上哪个更合适,或者我应该考虑其他选择吗?
我觉得你应该考虑这两件事,然后再做决定。
- 性能。大量读取,大量写入?测试哪个更好。
- 模型的成长。是否容易扩展?
什么是"table bloat"?您是否担心 table 太多?我工作过的许多 real-world 数据库都在 100 到 200 table 之间,因为这就是它所需要的。
如果您担心添加多个 table,那么为什么要为 User
、Appointment
和 [=12= 使用单独的 table ]?如果您有 User
的 multi-valued 属性,例如每个用户有多个 phone 号码,您会为 phone 创建一个单独的 table,还是您尝试以某种方式将它们全部组合到用户 table 中?或者为用户 phone、约会受邀者和任务里程碑提供多态 "things that belong to other things" table?
答案:不,您将创建一个 Phone
table,并使用它仅引用 User
table。如果 Appointments 有被邀请者,那会得到它自己的 table(可能是 appointments 和 users 之间的 many-to-many)。如果任务有里程碑,那也会有自己的 table。
正确的做法是像在应用程序中为对象类型建模一样为数据库建模table。您可能想阅读像 SQL and Relational Theory: How to Write Accurate SQL Code 3rd Edition by C. J. Date 这样的书,以了解有关 table 与类型的相似之处的更多信息。
您已经本能地知道无法创建外键这一事实是一个危险信号。外键必须仅引用一个父项 table。这应该是一个线索,表明它不是有效的关系数据库设计来制作多态外键。一旦您开始将 table 及其属性视为具体类型(如 SQL 和关系理论中所述),这将变得显而易见。
如果您必须创建一个笔记 table,您可以将其引用一个名为 "Notable" 的 table,它类似于 User
的超级 class 、Appointment
和 Task
。然后这三个 table 中的每一个也会引用 Notable
的主键。这模仿了多态性的 object-oriented 结构,您可以在其中有一个 class 注释通过其 superclass 类型引用一个对象。
但是恕我直言,这比需要的要复杂得多。我只想为 UserNotes
、AppointmentNotes
和 TaskNotes
创建单独的 table。我对多三个 table 并不感到困扰,它使您的代码更加清晰和可维护。
我正在处理具有某些类型(例如 User
、Appointment
、Task
等)的数据库,这些类型可以有零个或多个 Notes
关联每种类型。
我遇到的实现这些关系的可能解决方案是:
- 多态关系
- 每种类型table
多态关系
许多人认为这是最容易实现的解决方案,而且似乎是遵循 Active Record 模式的框架最常见的实现,我会添加一个 table,其数据是 morphable
:
我的 notable_type
可以让我区分 Note
相关的类型(User
、Appointment
、Task
),而 notable_id
将允许我在相关 type
table.
type
记录
优点:
- 易于扩展,更多的模型可以很容易地与多态关联class
- 限制table膨胀
- 一个 class 的结果可以被许多其他 classes 使用(DRY)
缺点
- 随着数据的增长,更多类型会使查询变得更加困难和昂贵
- 不能有外键
- 缺乏数据一致性
每种类型Table
或者,我可以为每种类型创建一个 table,它只负责与该类型关联的 Notes
。 type_id
外键可以让我快速获取个人 type
记录。
网上很多人认为这是一种代码味道,许多文章提倡避免多态关系,转而采用替代关系(例如here and here)。
优点:
- 允许我们有效地使用外键
- 高效的数据查询
- 保持数据一致性
缺点:
- 增加 table 膨胀,因为每种类型都需要单独的 table
- 结果为多个 class,每个代表单独的
type_notes
table
想法
多态关系当然是两个选项中更容易实现的一个,但是缺少外键约束并因此可能出现一致性问题让人感觉不对。
A table per notes
relationship (user_notes
, task_notes
etc.) with foreign keys seems the correct way (in keeping with design patterns) but could result在许多 table 中(添加其他可以具有 notes
的 types
或添加类似于 notes
的类型 [例如 events
])。
感觉我的选择要么简化table结构但放弃外键并增加查询开销,要么增加具有相同结构但简化查询并允许外键的table的数量键。
考虑到我的情况,以上哪个更合适,或者我应该考虑其他选择吗?
我觉得你应该考虑这两件事,然后再做决定。
- 性能。大量读取,大量写入?测试哪个更好。
- 模型的成长。是否容易扩展?
什么是"table bloat"?您是否担心 table 太多?我工作过的许多 real-world 数据库都在 100 到 200 table 之间,因为这就是它所需要的。
如果您担心添加多个 table,那么为什么要为 User
、Appointment
和 [=12= 使用单独的 table ]?如果您有 User
的 multi-valued 属性,例如每个用户有多个 phone 号码,您会为 phone 创建一个单独的 table,还是您尝试以某种方式将它们全部组合到用户 table 中?或者为用户 phone、约会受邀者和任务里程碑提供多态 "things that belong to other things" table?
答案:不,您将创建一个 Phone
table,并使用它仅引用 User
table。如果 Appointments 有被邀请者,那会得到它自己的 table(可能是 appointments 和 users 之间的 many-to-many)。如果任务有里程碑,那也会有自己的 table。
正确的做法是像在应用程序中为对象类型建模一样为数据库建模table。您可能想阅读像 SQL and Relational Theory: How to Write Accurate SQL Code 3rd Edition by C. J. Date 这样的书,以了解有关 table 与类型的相似之处的更多信息。
您已经本能地知道无法创建外键这一事实是一个危险信号。外键必须仅引用一个父项 table。这应该是一个线索,表明它不是有效的关系数据库设计来制作多态外键。一旦您开始将 table 及其属性视为具体类型(如 SQL 和关系理论中所述),这将变得显而易见。
如果您必须创建一个笔记 table,您可以将其引用一个名为 "Notable" 的 table,它类似于 User
的超级 class 、Appointment
和 Task
。然后这三个 table 中的每一个也会引用 Notable
的主键。这模仿了多态性的 object-oriented 结构,您可以在其中有一个 class 注释通过其 superclass 类型引用一个对象。
但是恕我直言,这比需要的要复杂得多。我只想为 UserNotes
、AppointmentNotes
和 TaskNotes
创建单独的 table。我对多三个 table 并不感到困扰,它使您的代码更加清晰和可维护。