0

我正在准备一个旧版 Microsoft SQL Server 数据库,以便我可以通过诸如实体框架之类的 ORM 进行交互,而我的问题围绕着处理我的一些共享通用类型的多对多关联的设置。具体来说,应该在主类型之间共享一个公共类型,还是每个主类型都有自己的链接表?

例如,这是我编写的一个简单示例,它显示了当前如何设置感兴趣的表:

遗留多对多

请注意,有两种类型,TeachersStudents,两者都可以包含零个、一个或多个PhoneNumbersTeachers和两个表Students实际上共享一个关联表 ( PeoplePhoneNumbers)。该字段FKID是 aTeacherId或 a StudentId

我认为应该设置的方式是这样的:

多对多更好?

这样,Teachers表和Students表都获得了自己的 PhoneNumbers 表。

我的直觉告诉我第二种方法是正确的方法。这是真的?即使 PhoneNumbers 表包含多个字段怎么办?我的面向对象程序员的大脑告诉我,如果这些表之间的唯一区别是它们链接到哪个主表,那么拥有几个相同的表是错误的,每个表都包含十几个字段?例如:

冗余表

在这里,我们有两个包含相同信息的表,但唯一的区别是一个表是地址,Teachers而另一个表是Students。这些对我来说是多余的,它们实际上应该是一个表——但是我失去了数据库约束它们的能力(对吗?),并且当我尝试将 ORM 应用于此时,也让我自己变得更加混乱。

这种类型的公共类型应该合并还是应该为每个主类型保持分离?

更新

下面的答案将我引向了以下解决方案,该解决方案基于数据库中的子类表。我最初的问题之一是我有一个在多个其他表之间共享的公用表,因为该实体类型对其他两个表都是通用的。处理这个问题的正确方法是对共享表进行子类化,并从本质上将它们从一个公共父级降级,并将公共数据类型链接到这个新的父级。这是一个示例(请记住,我的实际数据库与教师和学生无关,因此此示例是高度制造的,但概念是有效的):

解决方案

由于TeachersStudents都需要,因此解决方案是为表PhoneNumbers创建一个超类Party、 和 FK 。另请注意,您仍然可以对仅与. 在此示例中,我还对它们进行了子类化和向下一层,并将它们从.PhoneNumbersPartyTeachersStudentsStudentsPartTimeStudentsLearners

这个解决方案非常令人满意的地方是我在 ORM 中实现它,例如实体框架。

查询很容易。我可以使用特定电话号码查询所有教师和学生:

var partiesWithPhoneNumber = from p in dbContext.Parties
     where p.PhoneNumbers.Where(x => x.PhoneNumber1.Contains(phoneNumber)).Any()
     select p;

进行类似的查询同样容易,但仅适用于仅属于教师的电话号码:

var teachersWithPhoneNumber = from t in dbContext.Teachers
  where t.Party.PhoneNumbers.Where(x => x.PhoneNumber1.Contains(phoneNumber)).Any()
  select t;
4

4 回答 4

2

我认为您应该研究超类型/子类型模式。添加一个PartyPerson表,其中每个教师或学生都有一行。然后,使用and表PartyID 中的 PK 和 FK 返回(但将它们命名为and )。这在超类型表和每个子类型表之间建立了“一对零或一”的关系。TeacherStudentPartyTeacherIDStudentID

请注意,如果子类型表中有标识列,则需要将其删除。继续创建这些实体时,您首先必须插入超类型,然后在任一子类型中使用该行的 ID。

为了保持一致性,您还必须对其中一个子类型表重新编号,以使其 ID 不会与另一个表冲突。之后,您可以使用SET IDENTITY_INSERT ON创建缺少的超类型行。

所有这一切的美妙之处在于,当您有一个必须只允许一种类型的表时,例如Student您可以 FK 到该表,但是当您需要一个 FK 时——就像你的Address表一样——你可以对Party表进行 FK反而。

最后一点是将所有公共列移动到超类型表中,并且仅将列放在它们之间必须不同的子类型中。

您的单Phone表现在也很容易链接到PartyID

有关更详细的解释,请参阅此对类似问题的回答

于 2013-01-13T21:29:48.793 回答
2

教师和学生都是更一般概念(人)的子类。如果您创建一个包含为数据库中所有人共享的一般数据的 Person 表,然后创建链接到 Person 并包含任何其他详细信息的 Student 和 Teacher 表,您会发现您有适当的点可以链接到任何其他表。

如果存在对所有人都通用的数据(例如零到多个电话号码),那么您可以链接到 Person 表。当您拥有仅适用于学生的数据时,您可以将其链接到学生 ID。您获得了额外的优势,即学生教师只是一个同时拥有学生和教师记录的人。

一些 ORM 直接支持子类表的概念。LLBLGen 以我描述的方式这样做,因此您可以使您的数据访问代码与更高级别的概念(教师和学生)一起工作,并且将在低级别数据访问代码中代表您管理 Person 表。

编辑

对当前图表的一些评论(这可能与所翻译的源域无关,因此建议加一点盐)。

派对、教师和学习者看起来不错。如果您为费率添加开始日期和结束日期,那么薪水看起来不错,这样您就可以跟踪薪水历史记录。另请记住,如果您最终拥有多个具有薪水的实体,则使用 PartyID(而不是 TeacherID)可能是有意义的。

PartyPhoneNumbers 看起来您可以直接将电话号码挂在上面。这取决于您是否希望一次更改多人 (n:m) 的电话号码,或者电话号码是否由每一方独立拥有。(我希望是后者,因为您可能有一个学生是老师的(现实世界)孩子,因此他们共享一个电话号码。我不希望更新学生的电话号码来影响老师,所以连接表在这里看起来很奇怪。)

Learners to PaymentHistory 似乎是正确的,但 Student 与 PartTimeStudents 的差异似乎是人为的。(似乎 PartTimeStudents 更多的是 AttendenceDays,而这又是 LearnerClasses 加入的结果)。

于 2013-01-13T21:55:52.433 回答
1

您遇到的问题是“一个”关系的示例。一个人是老师或学生(或可能两者兼而有之)。

我认为现有的结构最能捕捉到这些信息。

这个人有一个电话号码。然后,有些人是老师,有些人是学生。每个实体的附加信息存储在教师表或学生表中。常用信息(例如姓名)位于电话表中。

将电话号码分成两个单独的表格是相当混乱的。毕竟,一个电话号码不知道是给学生还是老师。此外,您没有其他电话号码的空间,例如行政人员。对于有时可能会教授或帮助教授课程的学生,您也面临着挑战。

于 2013-01-13T21:03:39.880 回答
0

阅读您的问题,您似乎正在要求针对您的情况使用通用数据库架构。我过去见过几个,有些比其他更容易使用。

一种选择是让 Student_Address 表和 Teacher_Address 表都使用相同的 Address 表。这样,如果您有要存储的实体特定字段,您就有了这种能力。但这可能会稍微(尽管不是很明显)难以查询。

另一种选择是您在上面建议的方式——我可能只是在表上添加一个主键。但是,您希望将 PersonTypeId 字段添加到该表(链接到 PersonType 表的 PersonTypeId)。这样您就可以知道每条记录对应的是哪个实体。

我不建议有两个 PhoneNumber 表。我认为您会发现将所有内容都放在同一张表中会更容易维护。我更喜欢将相同的实体放在一起,这意味着 Student 是一个实体,Teachers 是一个实体,PhoneNumbers 是同一个实体。

祝你好运。

于 2013-01-13T21:06:59.670 回答