您的层次结构仍然不太合理,因为您在那里有两次相同的教师课程。我假设其中之一是Staff 而不是Teacher。但即便如此,我也不太清楚为什么老师也不会被视为员工?
在继续之前正确设计您的架构很重要。我将尝试回答您的实际问题,但这并不意味着这一定是解决问题的正确方法。最后我提出了一种替代架构设计供您考虑。
回答您的问题
为了回答你的问题,我们可以看看 ORM(即实体框架核心)是如何解决这种事情的。问题是,OOP 和类的概念并没有完全映射到关系数据库世界。 C# ORM 之类的 Entity Framework 必须这样做,所以即使您不打算使用 Entity Framework,看看他们如何解决这个问题可以让您深入了解。
为了更清楚地了解发生了什么,我将为您的每个类添加一些假列。让我们假设这是您的 C# 类的样子:
public class Employee
{
public string Id { get; set; }
public bool IsActive { get; set; }
}
public class Administrator : Employee
{
public string PhoneNumber { get; set; }
}
public class Teacher : Employee
{
public string HomeRoom { get; set; }
}
因此,您可以看到管理员和教师应该共享一个 ID 以及他们是否处于活动状态。管理员存储他们的电话号码,教师需要一个家庭房间。管理员不需要家庭房间,教师不需要电话号码。同样,这些列并不意味着是一个真实的用例,只是作为一个例子。
每个层次结构的表
第一种方法(也是 Entity Framework Core 中推荐/默认的方法)是执行single table per Hierarchy。这意味着您将有一个名为Employee 的表,该表将包含表示每种类型的员工(员工、教师、管理员等)所需的所有列。这意味着将有 1 个EmployeeID 列代表所有员工。神奇之处在于有一个“鉴别器”列来指定“此员工是 X”。
所以你的Employee 表看起来像这样:
| Column |
Type |
| Id |
NVARCHAR(50) PK |
| Discriminator |
NVARCHAR(100) (NOT NULL) |
| IsActive |
BIT (NOT NULL) |
| PhoneNumber |
NVARCHAR(50) (NULLABLE) |
| HomeRoom |
NVARCHAR(50) (NULLABLE) |
数据可能看起来像这样:
| Id |
Discriminator |
IsActive |
PhoneNumber |
HomeRoom |
| 1 |
Teacher |
1 |
NULL |
2A |
| 2 |
Administrator |
1 |
3256-6986 |
NULL |
在这种情况下,您的职责表将具有EmployeeID 的 FK,它将加入此表。从概念上讲,它很简单,但是您必须确保在对特定类型进行查询时始终添加鉴别器。使用 EF Core,这主要是为您处理的。
如果您想查询所有教师,您可以执行如下查询:
SELECT * FROM Employee WHERE Discriminator = 'Teacher'
这是 EF Core 中默认方式的原因是因为它允许更快地查询数据。关于这 1 类型的所有内容都存储在一个表中。这种好处也是它的弱点之一。您可以看到Teacher 行必须存储它的PhoneNumber 为空,即使该数据与教师无关。在这个小样本中这没问题,但是如果您有一堆员工类型,每个类型都有很多独特的列,那么您的表就会增加很多膨胀。这可能会让人很难理解发生了什么。
每种类型的表格
第二种方法是使用table per type。这样,您将拥有一个只有 Id 和 IsActive 的 Employee 表。然后会有一个单独的表供教师/管理员使用,其中包含这些课程的特定数据。
架构可能看起来像这样:
Employee
|专栏 |类型 |
|---- |-------|
|身份证 | NVARCHAR(50) PK 标识 |
|活跃 |位(非空)|
Administrator
|专栏 |类型 |
|---- |-------|
|员工 ID | NVARCHAR(50) PK |
|电话号码 | NVARCHAR(50) (NULLABLE) |
Teacher
|专栏 |类型 |
|---- |-------|
|员工 ID | NVARCHAR(50) PK |
|主页房间 | NVARCHAR(50) (NULLABLE) |
显示数据的样子有点困难,但基本上你会让每个员工都在员工表中。那么如果他们是老师,Teacher 表中就会有一条与 EmployeeId 匹配的记录。
所以如果你想用这个方法查询所有的老师,你可以这样做:
SELECT * FROM Employee
INNER JOIN Teacher ON Teacher.EmployeeId = Employee.Id
这种方式的好处是您不会存储任何浪费的数据。
不利的一面是,您可能会通过添加大量联接来使查询变得更加复杂。 In the EF Core docs they mention this performance hit.
另类
我在开头提到我会尝试按原样回答您的问题,我已经这样做了。但我也认为提供可能适用也可能不适用的替代方案很重要。对我来说,您似乎是从 OOP 方法开始希望数据的外观,然后您会问“我如何将其映射到数据库”。但是,OOP 设计并不能真正 100% 使用模式,因此请从模式开始,然后弄清楚如何用类来表示它。
考虑以下问题:员工是否可以同时担任多个角色?使用您当前的系统,某人被锁定为一种类型的员工。如果有人换了角色怎么办?也许他们从管理员开始,但变成了老师?我并不是说这绝对是一个问题。也许您会考虑将某人换工作作为新员工,在这种情况下,您当前的方式会奏效。
我建议以完全不同的方式构建它。让您的Employee 表包含所有员工可能需要的所有信息(无论他们担任什么角色)。然后有一个单独的表,名为Role,其中包含所有可能的不同类型员工的行。例如,它会有一行代表“教师”、“管理员”、“场地管理员”等。这样添加新角色就像在表格中添加一行一样简单。
然后你会有一个名为EmployeeRole 的多对多表,它只存储EmployeeID 和RoleID(也许还有一个IsActive 来说明他们是否还在那个角色中)。这样,一名员工可以担任多个角色,并且您可以了解他们之前担任过的角色的历史记录。
然后你可以让你的Duty 表有一个到这个EmployeeRole 表的 FK,而不是直接到 Employee 表。员工拥有的每个角色都会有一组不同的职责。
虽然我不是数据库设计师,但这种设计绝对不是没有自己的缺陷,因此请考虑您的用例并仔细设计您需要允许的限制。不要用 OOP 术语来考虑你的代码,从你的模式中创建一个类结构。用我的例子来换一种方式思考这个问题,但不要盲目地遵循我的建议。
总结
如果您想坚持原来的设计,我认为 Table-Per-Hierarchy 最适合您。但我强烈建议您重新考虑您的架构并弄清楚适用于它的限制类型,以及您是否需要考虑这些限制。