你如何有效地build模数据库中的inheritance?
在数据库中build模inheritance的最佳实践是什么?
什么是权衡(例如可查询性)?
(我最感兴趣的是SQL Server和.NET,但我也想了解其他平台如何解决这个问题。)
在数据库中build模有几种方法。 你select哪一个取决于你的需求。 这里有几个选项:
每桌types(TPT)
每个class级都有自己的桌子。 基类中包含了所有的基类元素,每个从它派生出来的类都有自己的表,主键也是基类表的外键; 派生表的类只包含不同的元素。
举个例子:
class Person { public int ID; public string FirstName; public string LastName; } class Employee : Person { public DateTime StartDate; }
会导致像这样的表格:
table Person ------------ int id (PK) string firstname string lastname table Employee -------------- int id (PK, FK) datetime startdate
每个层级(TPH)
有一个表格表示所有的inheritance层次结构,这意味着几列可能是稀疏的。 鉴别器列被添加,它告诉系统这是什么types的行。
鉴于上面的类,你最终得到这个表:
table Person ------------ int id (PK) int rowtype (0 = "Person", 1 = "Employee") string firstname string lastname datetime startdate
对于任何行types为0(Person)的行,startdate将始终为空。
每个混凝土表(TPC)
每个类都有自己完整的表格,没有任何其他表格的引用。
鉴于上面的类,你最终得到这些表:
table Person ------------ int id (PK) string firstname string lastname table Employee -------------- int id (PK) string firstname string lastname datetime startdate
正确的数据库devise就像适当的对象devise。
如果您打算将数据库用于除了简单序列化对象之外的任何事情(例如报告,查询,多应用程序使用,商业智能等),那么我不推荐从对象到表格的简单映射。
许多人认为数据库表中的一行是一个实体(我花了很多年的时间思考),但是一行不是实体。 这是一个命题。 数据库关系(即表格)代表了关于世界的一些事实陈述。 行的存在表明事实是真实的(相反,它的缺席表明事实是错误的)。
有了这个理解,你可以看到一个面向对象程序中的单一types可能存储在十几个不同的关系中。 而且各种types(通过inheritance,关联,聚合或完全无关联)可以部分地存储在一个关系中。
最好问自己,你想存储什么样的事实,你想要回答什么问题,你想要产生什么样的报告。
一旦创build了正确的数据库devise,创build查询/视图就可以轻松地将对象序列化为这些关系,这是一件简单的事情。
例:
在酒店预订系统中,您可能需要储存Jane Doe在4月10日至12日在Seaview Inn酒店预订房间的事实。 这是客户实体的属性吗? 这是酒店实体的属性吗? 它是否包含客户和酒店的预订实体? 它可以是面向对象系统中的任何或所有这些东西。 在数据库中,这些都不是。 这仅仅是一个事实。
要查看差异,请考虑以下两个查询。 (1)Jane Doe明年有几次酒店预订? (2)4月10日在Seaview Inn预订了多less间客房?
在面向对象系统中,query(1)是客户实体的属性,query(2)是酒店实体的属性。 这些是将在其API中公开这些属性的对象。 (显然,获得这些价值观的内在机制可能涉及对其他对象的引用。)
在关系数据库系统中,这两个查询都会检查预留关系以获得它们的数量,从概念上讲,不需要打扰任何其他“实体”。
因此,试图存储关于世界的事实 – 而不是试图存储具有属性的实体 – 构build适当的关系数据库。 而且一旦devise了属性,那么在devise阶段就可以轻松地构build出有用的查询,因为满足这些查询所需的所有事实都处于适当的位置。
简短的回答:你不。
如果你需要序列化你的对象,使用ORM,或者更好的东西像主动logging或prevaylence。
如果您需要存储数据,请以关系方式存储数据(小心存储的内容,注意Jeffrey L Whitledge刚才所说的内容),而不是受您的对象devise影响。
您可以在数据库中设置两种主要的inheritancetypes,每个实体的表和每个层次结构的表。
每个实体的表就是你有一个基本实体表,它具有所有子类的共享属性。 然后,每个孩子class级都有另一个表格,每个表格只有适用于该class级的属性。 他们被PK的1:1联系起来
替代文字http://mattlant.com/ent.png
每层次的表是所有类共享一个表的地方,可选属性是可空的。 他们也是一个鉴别字段,它是一个数字,表示该logging当前拥有的types
替代文字http://mattlant.com/hier.png SessionTypeID是辨别器
每个层次的目标查询速度更快,因为你不需要连接(只有鉴别器的值),而每个实体的目标你需要做复杂的连接,以检测什么types的东西,以及撤回所有的数据。
编辑:我在这里显示的图像是我正在工作的项目的屏幕截图。 资产图像不完整,因此它的空白,但主要是显示它的设置,而不是放在你的表内。 那取决于你 ;)。 会话表保存虚拟协作会话信息,根据涉及的协作types可以有几种types的会话。
TPT,TPH和TPC模式是你走的路,正如Brad Wilson所说的。 但是几个注释:
-
从基类inheritance的子类可以看作是数据库中基类定义的弱实体,这意味着它们依赖于它们的基类,没有它就不能存在。 我已经看到了次数,每个子表都存储了唯一的ID,同时也将FK保存到父表中。 一个FK就足够了,甚至可以更好地删除子表和基表之间的FK关系。
-
在TPT中,通过仅查看基表logging,您无法findlogging所代表的子类。 这有时是需要的,当你想加载一个所有logging的列表(没有
select
每个子表)。 处理这个问题的方法之一是有一列表示子类的types(类似于TPH中的rowType字段),因此以某种方式混合TPT和TPH。
假设我们要devise一个拥有以下形状类图的数据库:
public class Shape { int id; Color color; Thickness thickness; //other fields } public class Rectangle : Shape { Point topLeft; Point bottomRight; } public class Circle : Shape { Point center; int radius; }
上述类的数据库devise可以是这样的:
table Shape ----------- int id; (PK) int color; int thichkness; int rowType; (0 = Rectangle, 1 = Circle, 2 = ...) table Rectangle ---------- int ShapeID; (FK on delete cascade) int topLeftX; int topLeftY; int bottomRightX; int bottomRightY; table Circle ---------- int ShapeID; (FK on delete cascade) int centerX; int center; int radius;
你会正常化你的数据库,这实际上是镜像你的inheritance。 它可能会有性能下降,但这正是如此。 你可能将不得不使用良好的常识来find平衡。
重复类似的线程答案
在OR映射中,inheritance映射到父表和子表使用相同标识符的父表
例如
create table Object ( Id int NOT NULL --primary key, auto-increment Name varchar(32) ) create table SubObject ( Id int NOT NULL --primary key and also foreign key to Object Description varchar(32) )
SubObject与Object有一个外键关系。 当您创build一个SubObject行时,您必须首先创build一个Object行并在两行中使用Id
编辑:如果你正在寻找模型的行为也需要一个types表列出了表之间的inheritance关系,并指定实现每个表的行为的程序集和类的名称
似乎是矫枉过正,但这一切都取决于你想要使用它!
使用SQL ALchemy(Python ORM),可以执行两种types的inheritance。
我有经验的人使用单表,并有一个判别列。 例如,一个Sheep数据库(没有玩笑!)将所有Sheep存储在一个表中,并且使用该表中的性别列来处理Rams和Ewes。
因此,你可以查询所有的羊,并得到所有的羊。 或者你只能通过Ram查询,而且只会得到公羊。 你也可以做一些事情,比如有一个只能是Ram的关系(即羊的父亲),等等。
请注意,一些数据库引擎已经提供了像Postgres一样的本地inheritance机制。 看看文档 。
例如,您可以查询上述响应中描述的Person / Employee系统,如下所示:
/ *这显示所有人或雇员的名字* / SELECT firstname FROM Person; / *仅显示所有员工的开始date* / SELECT startdate FROM Employee;
那就是你的数据库的select,你不需要特别聪明!