【发布时间】:2011-09-19 01:49:53
【问题描述】:
如果我有用户和用户组(如本地天文组/俱乐部),并且我希望两者都与街道地址建立一对多关系,我可以只拥有 1 个地址表和两个 fk 吗?我不必复制表架构?还是只有 2 个单独的表 user_addresses 和 user_group_addresses 是更好的做法?感谢您的投入和时间,谢谢!
【问题讨论】:
标签: php mysql database database-design doctrine
如果我有用户和用户组(如本地天文组/俱乐部),并且我希望两者都与街道地址建立一对多关系,我可以只拥有 1 个地址表和两个 fk 吗?我不必复制表架构?还是只有 2 个单独的表 user_addresses 和 user_group_addresses 是更好的做法?感谢您的投入和时间,谢谢!
【问题讨论】:
标签: php mysql database database-design doctrine
如果用户和组的地址实际上是同一类型的地址,则得到一个地址表,您可以根据需要参考。
【讨论】:
您描述的具有两个外键的设计称为独占弧。应该只填充两个外键之一。执行和使用非常尴尬。
例如,地址必须引用一个实体,因此从概念上讲,该列是强制性的,并且不应为 NULL。但是您不能将两列都设为 NOT NULL,因为其中之一与给定地址无关。所以它们必须是可空的。然后你必须有一些其他的方法来防止两者都为 NULL,并防止两者都为非 NULL。 MySQL 不支持 CHECK 约束,因此您可以编写触发器或编写自定义应用程序代码来强制执行此规则。
如果创建一个地址表,但颠倒关系呢?也就是说,Users 和 Groups 表包含对 Addresses 表的外键引用,而不是相反。
另一种解决方案是让用户和组都依赖于一个共同的超级表,称之为“Addressables”之类的。就像 OO 设计中的接口或抽象类。然后,您的地址也可以具有 Addressables 的外键。请参阅other questions I have answered on this subject 中的示例。
我还在我的书SQL Antipatterns, Avoiding the Pitfalls of Database Programming“多态关联”一章中更详细地介绍了这个问题。
【讨论】:
最好有 1 个地址表,除非一种地址类型对另一种地址类型有特殊需求。这更容易维护,还允许您添加功能。例如,如果您当地的天文学小组决定他们想要在某处拥有“事件”,那么您只需创建事件表并引用地址表就可以了。如果您将它们分开,那么每次您有一个具有地址的新“实体”时,您都必须创建一个新表。
希望这会有所帮助。
至于您的评论:我会将参考放在单独的参考表中。
【讨论】:
数据库设计的首要任务是有效性。在使用单表策略持久化所有地址时,会隐含地定义地址属于用户还是组。
因为您不必识别哪些地址是所需的集合(用户或组),所以两表策略是一种更简单的编程方法,可以防止有人编写错误的代码 (SQL)。
例如,我们需要一些来自用户地址的数据:
SELECT * FROM user_address WHERE <other conditions>; // The two-table strategy
/**
* The one-table strategy
*/
SELECT * FROM all_addresses
WHERE user_id IS NOT NULL
AND <other conditions>;
此外,如果我们使用两个表来保存任何需要的地址,性能会更好。
在单表策略中,即使该列中有索引(取决于数据库系统),也可能不会优化“IS NOT NULL”条件。 Join 是单表策略中识别用户地址的另一种方法,但它仍然比另一种策略更加努力。
不过,单表策略有其性能优势。如果我们需要收集所有地址(无论是用户还是组),这种操作就是性能 系统瓶颈,可以考虑使用一表策略。
【讨论】: