拥有几个互斥的一对一关系是不好的做法吗?


38

比方说,一个表car有一个一对一关系的表electric_cargas_carhybrid_car。如果a carelectric_car,则不能再出现在gas_car或中hybrid_car,等等。

这样的设计有什么问题吗?将来可能会出现一些问题?

Answers:


59

不同类型的汽车是一个普遍问题的实例,该问题在数据建模中一遍又一遍地浮出水面。在ER建模中称为“一般化/专业化”,在对象建模中称为“超类/子类”。

对象建模器使用对象模型中内置的继承功能来轻松解决问题。子类只是扩展超类。

关系建模者面临一个问题。如何设计表以模仿继承带来的好处?

最简单的技术称为单表继承。有关所有类型汽车的数据被分组到一个汽车表中。有一个列car_type,将一个类型的所有汽车组合在一起。汽车不能属于一种以上的类型。如果某列与电动汽车无关,则在与电动汽车有关的行中将其保留为NULL

这种简单的解决方案适用于较小和较简单的情况。大量NULL的存在会增加一点存储开销,并增加一些检索开销。如果对可空列进行布尔测试,则开发人员可能必须学习SQL三值逻辑。一开始这可能令人困惑,但是一个人已经习惯了。

还有另一种技术,称为类表继承。在此设计中,除了用于所有表的合并表car之外,还有用于gas_car,electric_car和hybrid_car的单独表。当您需要有关特定类型汽车的所有数据时,可以将汽车表与适当的专用表结合在一起。此设计中的NULL较少,但是您进行的连接更多。在较大和更复杂的情况下,此技术效果更好。

第三种技术称为共享主键。此技术通常与类表继承结合使用。子类的专用表具有car表中相应条目的主键副本作为其主键。可以将此id列声明为主键和外键。

当要添加新车时,这涉及一些额外的编程,但是这使连接变得简单,容易和快速。

超级类和子类在现实世界中始终存在。不要害怕 但是请测试您的初始设计的性能。如果您的第一次尝试既简单又合理,则可以对其进行调整以加快速度。


3
哇谢谢你!那就是我要弄清楚的地方。类表继承似乎正是我所需要的。我更改了我为以后的读者所接受的答案,因为我认为它完全涵盖了问题,而不仅仅是我的情况。
亚瑟·塔拉索夫

6
优秀的答案在这里。提示:彻底记录这些设计决策。无论采用哪种方法,当有人检查数据库结构时,它都不是显而易见的。一些数据库(例如Postgres)使您能够将注释与列,表等的元数据绑定在一起
罗勒·布尔克

您没有解决限制电动汽车也不能成为混合动力汽车的限制。您需要一个单独的表。
jmoreno

2
你是对的。如果将car_type字段添加到cars表,则可以将cars限制为仅属于一种类型,但要以偏离完全规范化为代价。一个好的DBMS可以让您定义一个检查约束,以防止一辆车被输入多个以上的专用表中。有一些开销,您要添加新车。
Walter Mitty

@WalterMitty但没有car_type字段,检索数据时您如何知道要在哪个表中查找详细信息?您是否必须阅读所有三个表以查看哪个表具有有关该特定car记录的数据?
乔什(Josh Part)

12

在模型中拥有足够多的实体子类型以反映您要建模的数据的真实性并没有错。问题不是子类型是否是一种不好的做法。问题可能是它是一个好的模型吗?

例如,在您的示例中,您如何使用奥迪A4 eTron等插电式混合动力车?这是“电动汽车”还是“混合动力汽车”?

您还必须问自己的另一个问题是,为什么您根本要分型?您的子类型中有多少个不同的谓词?这些谓词是否在子类型之间共享?情况可能变得复杂。

数据库设计中未使用子类型进行分类。您可以使用代码,代码表的外键或标志进行分类。子类型用于为不同类型的关注事物建模不同的谓词集。如果您仅将子类型用于分类,那么这是一个不好的做法。

如果您的子类型清楚明确地为数据库关心的事物建模不同的谓词集,那么这是一个很好的实践,无论您需要多少个子类型。


谢谢,我担心自己在为自己设置陷阱。我的问题是每个子类型都会有很多列。有些会重叠,我会把它们放在一个car表中,但是很多不会,并且会放在子类型表中。例如,它将类似于存储汽车类型的基本部件。电动汽车发动机可以有100份,汽油汽车发动机有75份,混合动力有125份。50份将是常见的,并存储在cars,而50,25和75将在electric_cargas_carhybrid_car
亚瑟塔拉索夫
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.