当前位置:  开发笔记 > 编程语言 > 正文

你如何有效地模拟数据库中的继承?

如何解决《你如何有效地模拟数据库中的继承?》经验,为你挑选了4个好方法。

在数据库中建模继承的最佳实践是什么?

权衡取舍(例如可疑性)是什么?

(我对SQL Server和.NET最感兴趣,但我也想了解其他平台如何解决这个问题.)



1> Brad Wilson..:

有几种方法可以在数据库中建模继承.您选择哪个取决于您的需求.以下是一些选项:

每表类型(TPT)

每个类都有自己的表.基类中包含所有基类元素,并且从中派生的每个类都有自己的表,主键也是基类表的外键.派生表的类只包含不同的元素.

例如:

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)

有一个表代表所有继承层次结构的表,这意味着几个列可能是稀疏的.添加一个鉴别器列,告诉系统这是什么类型的行.

鉴于上面的类,你最终得到这个表:

table Person
------------
int id (PK)
int rowtype (0 = "Person", 1 = "Employee")
string firstname
string lastname
datetime startdate

对于任何行类型0(Person)的行,startdate将始终为null.

每混凝土表(TPC)

每个类都有自己的完全形成的表,没有任何其他表的引用.

鉴于上述类,您最终得到这些表:

table Person
------------
int id (PK)
string firstname
string lastname

table Employee
--------------
int id (PK)
string firstname
string lastname
datetime startdate


只有DBA才会假定非规范化总是错误的.:)
"你选择哪个取决于你的需求" - 请详细说明,因为我认为选择的原因构成了问题的核心.
看看我对这个问题的评论.为存在的Rdb技术术语使用有趣的新名称会导致混乱."TPT"是超类型子类型."TPH"是非标准化的,是一个严重错误."TPH"甚至更不规范,另一个严重错误.
虽然我会承认非规范化会在某些情况下导致性能提升,但这完全是由于DBMS中数据的逻辑和物理结构之间的不完整(或不存在)分离.不幸的是,大多数商业DBMS都存在这个问题.@PerformanceDBA是正确的.非现代化是一种判断错误,牺牲了速度的数据一致性.遗憾的是,如果DBMS设计得当,DBA或开发人员永远不需要做出选择.为了记录,我不是DBA.
@Brad Wilson.只有开发人员会对"性能"或其他方面进行非规范化.通常,它不是去标准化,事实是它是非标准化的.去标准化或非标准化是一个错误,是一个事实,在理论的支持下,并且经历了数百万人的经历,它不是"假设".
与数据库中的大多数内容一样,您需要权衡存储成本与性能。TPH将所有内容存储在一张表中,因此选择速度很快。TPT比TPC更为紧凑,但是由于连接的缘故,其价格更高。在TPC中查找内容意味着检查多个表。这些都需要权衡,并且您的决定必须基于对您的应用程序最重要的决定。
无论您选择哪种方法,您还可以添加视图以简化它.假设您选择只有一个表(TPH),但同时您希望只能显示一种类型.在这种情况下,您可以为每种类型创建一个视图.TPH的好处在于您不需要连接表,也可以轻松订购结果.坏的是你有多个NULL的宽表.您可以检查这取决于实体的类型,使用检查约束,某些列不应为null(http://msdn.microsoft.com/en-us/library/ms179491%28v=sql.105%29.aspx) .

2> Jeffrey L Wh..:

适当的数据库设计与正确的对象设计完全不同.

如果您计划将数据库用于除了简单序列化对象之外的任何事情(例如报表,查询,多应用程序使用,商业智能等),那么我不建议从对象到表格的任何简单映射.

许多人认为数据库表中的一行是一个实体(我花了很多年思考这些术语),但是一行不是一个实体.这是一个命题.数据库关系(即表格)表示关于世界的一些事实陈述.行的存在表明事实是真的(相反,它的缺席表明事实是错误的).

通过这种理解,您可以看到面向对象程序中的单个类型可以存储在十几个不同的关系中.并且可以将多种类型(通过继承,关联,聚合或完全无关联联合)部分地存储在单个关系中.

最好问自己,你想要存储什么事实,你想要答案的问题是什么,你想要产生什么样的报告.

一旦创建了正确的数据库设计,那么创建允许您将对象序列化为这些关系的查询/视图是一件简单的事情.

例:

在酒店预订系统中,您可能需要存储Jane Doe预订4月10日至12日Seaview Inn酒店的客房.这是客户实体的属性吗?它是酒店实体的属性吗?它是一个包含客户和酒店属性的预订实体吗?它可以是面向对象系统中的任何或所有这些东西.在数据库中,它不是那些东西.这简直是​​一个事实.

要查看差异,请考虑以下两个查询.(1)Jane Doe明年有多少酒店预订?(2)4月10日在Seaview Inn预订了多少间客房?

在面向对象的系统中,查询(1)是客户实体的属性,查询(2)是酒店实体的属性.这些是在API中公开这些属性的对象.(尽管显然,获取这些值的内部机制可能涉及对其他对象的引用.)

在关系数据库系统中,两个查询都会检查保留关系以获取其数字,并且在概念上不需要打扰任何其他"实体".

因此,它试图存储关于世界的事实 - 而不是试图存储具有属性的实体 - 构建适当的关系数据库.一旦设计得当,就可以轻松构建在设计阶段无意义的有用查询,因为完成这些查询所需的所有事实都在适当的位置.


+1最后,一个在无知之海中真正知识的岛屿(拒绝在他们的范围之外学习任何东西).同意,这不是魔术:如果RDb是使用RDb priciples设计的,那么"映射"或"投射"任何"类"都是毫不费力的.将RDb强制转换为基于类的要求是完全错误的.
这是一个真正的答案宝石.它需要一些时间来真正沉入并需要一些练习才能正确,但它已经影响了我对关系数据库设计的思考过程.
@ sevenforce-数据库设计实际上取决于系统的要求,但没有给出.没有足够的信息来提供决定.在许多情况下,类似于"每种类型的表"设计可能是适当的,如果不是盲目跟随.例如,start-date可能是Employee对象的一个​​很好的属性,但在数据库中它应该是Employment表中的一个字段,因为一个人可以多次雇用多个开始日期.这对于对象(使用最新的对象)无关紧要,但在数据库中这很重要.

3> Marcin..:

简短的回答:你没有.

如果您需要序列化对象,请使用ORM,甚至更好的东西,如activerecord或prevaylence.

如果您需要存储数据,请以关系方式存储(注意存储的内容,并注意Jeffrey L Whitledge刚才所说的内容),而不是受到对象设计的影响.


+1尝试在数据库中建模继承是浪费良好的关系资源.

4> 小智..:

如Brad Wilson所述,TPT,TPH和TPC模式是你的方式.但几个笔记:

从基类继承的子类可以看作是数据库中基类定义的弱实体,这意味着它们依赖于它们的基类,没有它就不能存在.我已经看过很多次,为每个子表存储唯一的ID,同时将FK保存到父表.一个FK就足够了,对于子表和基表之间的FK关系具有on-delete级联启用甚至更好.

在TPT中,仅通过查看基表记录,您无法找到记录所代表的子类.当您想要加载所有记录的列表(不对 select 每个子表执行操作)时,有时需要这样做.处理此问题的一种方法是使用一列表示子类的类型(类似于TPH中的rowType字段),因此以某种方式混合TPT和TPH.

假设我们要设计一个包含以下形状类图的数据库:

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;
}

上述类的数据库设计可以是这样的:

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;

推荐阅读
放ch养奶牛
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有