在数据库中建模继承

时间:2009-01-06 17:10:54

标签: database inheritance

对于数据库分配,我必须为学校建模系统。部分要求是为员工,学生和家长建模信息。

在UML类图中,我将其建模为这三个类是人类型的子类型。这是因为他们都需要有关地址数据的信息。

我的问题是:如何在数据库(mysql)中对此进行建模?

到目前为止的想法如下:

  1. 创建一个单片人员表,其中包含每种类型的所有信息,并且根据存储的类型,将包含许多空值。 (我怀疑这对讲师来说会很顺利,除非我说服非常令人信服)。
  2. 一个有三个外键的人员表,它引用了这些子类型但其中两个是空的 - 实际上我甚至不确定这是否有意义或是否可能?
  3. 根据这个wikipage about django,可以按如下方式在子类型上实现主键:

    "id" integer NOT NULL PRIMARY KEY REFERENCES "supertype" ("id")
  4. 我还没有想到的其他东西......
  5. 对于那些之前在数据库中建模继承的人;你是怎么做到的?你推荐什么方法?为什么?

    非常欢迎链接到文章/博客文章或以前的问题。

    谢谢你的时间!

    更新

    好的,谢谢大家的答案。我已经有一个单独的地址表,所以这不是问题。

    干杯,

    亚当

8 个答案:

答案 0 :(得分:5)

4桌工作人员,学生,家长和人员为通用的东西。 工作人员,学生和家长都有外键,每个人都会回复人(而不是相反)。

人员有一个字段,用于标识此人的子类(即员工,学生或家长)。

编辑:

正如HLGM所指出的,地址应该存在于一个单独的表中,因为任何人都可能有多个地址。 (但是 - 我不同意自己 - 你可能希望故意将地址限制为每人一个,限制邮件列表的选择等。)

答案 1 :(得分:2)

嗯,我认为所有方法都是有效的,任何一位在一张桌子上将其推倒的讲师(除非要求具体说你不应该这样做)正在取消一个可行的策略,因为他们个人的意见。

我强烈建议您查看有关NHibernate的文档,因为这提供了执行上述操作的不同方法。我现在试图拙劣地鹦鹉学舌。

您的选择:

  • 1)一个表中包含具有“分隔符”列的所有数据。这一栏说明了这个人是什么样的人。这在简单的情况下是可行的,并且(严重地)高性能,其中连接会伤害太多
  • 2)每个类的表会导致列的重复,但会再次避免连接,所以它的简单和快速(尽管在大多数情况下只有lil和indexing可以减轻这种情况)。
  • 3)“正确”的继承。标准化版本。你几乎就在那里,但你的钥匙在错误的地方IMO。您的Employee表应包含PersonId,以便您可以执行以下操作:

    从employee.personId = person.personId

  • 中的员工内部联系人员中选择employee.id,person.name

获取员工姓名仅在人员表上指定的所有员工姓名。

答案 2 :(得分:2)

我会去#3。

您的目标是给讲师留下深刻印象,而不是PM或客户。学者倾向于不喜欢无效,并且可能(潜意识地)惩罚你使用其他方法(依赖于空值)。

并且您不一定需要django扩展(PRIMARY KEY ... REFERENCES ...)您可以使用普通的FOREIGN KEY。

答案 3 :(得分:1)

“那么对于之前在数据库中建模继承的人来说,你是怎么做到的?你推荐什么方法?为什么? “

方法1和3都很好。差异主要在于您的用例。

1)适应性 - 哪个更容易改变?几个与父表具有FK关系的独立表。

2)性能 - 需要更少的连接?一张单桌。

大鼠。没有设计可以完成两者。

此外,除了您的单表和FK到父母之外,还有第三种设计。

三个单独的表,包含一些公共列(通常是所有子类表中的超类列的复制和粘贴)。这非常灵活且易于使用。但是,它需要三个表的并集来组合整个列表。

答案 4 :(得分:0)

OO数据库经历了相同的事情,并提出了几乎相同的选项。

如果重点是在数据库中建模子类,那么您可能已经在考虑我在真实OO数据库中看到的解决方案(将字段留空)。

如果没有,您可能会考虑创建一个不以这种方式使用继承的系统。

继承应该总是非常谨慎地使用,这可能是一个非常糟糕的情况。

一个好的准则是永远不要使用继承,除非你实际上有一些代码与“Parent”类的字段不同,而不是“Child”类中的相同字段。如果您的类中的业务代码没有专门引用字段,那么该字段绝对不应该导致继承。

但是,如果你在学校,那可能与他们想要教的东西不符......

答案 5 :(得分:0)

出于分配目的,“正确”答案可能是#3:

Person
PersonId Name Address1 Address2 City Country

Student
PersonId StudentId GPA Year ..

Staff
PersonId StaffId Salary ..

Parent
PersonId ParentId ParentType EmergencyContactNumber ..

其中PersonId始终是主键,也是最后三个表中的外键。

我喜欢这种方法,因为它可以很容易地代表具有多个角色的同一个人。例如,老师也可以成为父母。

答案 6 :(得分:0)

我建议五张桌子 人 学生 员工 亲 地址

为什么 - 因为人们可以有多个广告,人们也可以拥有多个角色,而且您希望员工获得的信息与您为父母或学生存储所需的信息不同。

此外,您可能希望将名称存储为last_name,Middle_name,first_name,Name_suffix(如jr。)而不是名称。相信我你会想要在last_name上搜索!名称不是唯一的,因此您需要确保拥有唯一的代理主键。

在尝试设计数据库之前,请先阅读有关规范化的信息。这是一个开始的来源: http://www.deeptraining.com/litwin/dbdesign/FundamentalsOfRelationalDatabaseDesign.aspx

答案 7 :(得分:0)

应该像这样创建超级类型的人:

CREATE  TABLE Person(PersonID int primary key, Name varchar ... etc ...)

所有子类型都应该像这样创建:

CREATE  TABLE IF NOT EXISTS Staffs(StaffId INT NOT NULL ,
  PRIMARY KEY (StaffId) ,  
  CONSTRAINT FK_StaffId FOREIGN KEY (StaffId) REFERENCES Person(PersonId)
  )

CREATE  TABLE IF NOT EXISTS Students(StudentId INT NOT NULL ,
  PRIMARY KEY (StudentId) ,  
  CONSTRAINT FK_StudentId FOREIGN KEY (StudentId) REFERENCES Person(PersonId)
  )

CREATE  TABLE IF NOT EXISTS Parents(PersonID INT NOT NULL ,
  PRIMARY KEY (PersonID ) ,  
  CONSTRAINT FK_PersonID  FOREIGN KEY (PersonID ) REFERENCES Person(PersonId)
  )

子类型中的外键员工,学生,家长增加了两个条件:

  1. 除非对应的子类型行,否则无法删除人员行 不被删除。对于例如如果学生有一个学生入学 表引用Person表,不删除学生条目 人员入境不能删除,这一点非常重要。如果学生 创建对象然后不删除Student对象我们不能 删除基础人物对象。

    1. 所有基类型都有外键" not null"确保每个基地 类型将始终存在基类型。对于例如如果你创建 学生对象必须先创建Person对象。