2

我有一个问题可以总结如下:

假设我正在实现一个员工数据库。对于每个人取决于他的职位,应填写不同的字段。因此,例如,如果员工是软件工程师,我有以下列:

Name
Family
Language
Technology
CanDevelopWeb 

如果员工是业务经理,我有以下列:

Name
Family
FieldOfExpertise
MaximumContractValue
BonusRate

如果员工是销售人员,那么其他一些列等等。

如何在数据库模式中实现这一点?

我认为的一种方法是有一些相关的表:

核心表:

Name
Family
Type

如果 type 是 one 则员工是软件开发人员,因此剩余信息应在表中SoftwareDeveloper

Language
Technology
CanDevelopWeb 

对于业务经理,我有另一个包含列的表:

FieldOfExpertise
MaximumContractValue
BonusRate

这种结构的问题是我不确定如何在表之间建立关系,因为一个表与一列上的多个表有关系。

如何执行关系完整性?

4

7 回答 7

3

这里有一些思想流派。

(1) 将可为空的列存储在单个表中并仅填充相关的列(检查约束可以在此处强制执行完整性)。有些人不喜欢这个,因为他们害怕 NULL。

(2) 您的多表设计,其中每种类型都有自己的表。使用 DRI 更难强制执行,但对于应用程序或触发逻辑可能微不足道。

其中任何一个的唯一问题是,一旦添加了新属性(CanReadUpsideDown如2)如果代表新的“类型”员工,您需要添加一个新表。

(3) EAV,您有一个存储属性名称和值对的表。您在这里对数据完整性的控制较少,但您当然可以将属性名称限制为某些字符串。我在这里写了这个:

于 2012-06-05T20:59:51.313 回答
2

您正在描述实现类别(又名继承、泛化、子类)层次结构的3 种可能策略中的一种(“每个表的类” )。

PK 从父表到子表的正确“传播”自然是由它们之间的直接外键强制执行的,但确保子行的存在性和排他性是另一回事。它可以完成(如上面的链接中所述),但增加的复杂性可能不值得,我通常建议在应用程序级别处理它。

于 2012-06-05T21:02:34.213 回答
1

在这种情况下,没有任何现有的 SQL 引擎可以让您的生活变得轻松。

您的问题在“实体子类型化”一章的“数据库管理中的实际问题”中进行了相当大的讨论。值得称赞的阅读,不仅适用于这一章。

从逻辑设计的角度来看,正确的解决方案与您的解决方案相似,但针对核心表中的“类型”列。您不需要那个,因为您可以从员工出现的非核心表中派生“类型”。

您需要查看的是业务规则,也就是数据约束,它将确保数据的整体完整性(也就是一致性)(当然,这些是否实际适用是您的业务用户而不是我应该告诉您的) :

每个指定的员工必须只有一份工作,因此在某处有一些工作细节。iow :(1)没有任何工作细节的指定员工和(2)没有> 1个工作细节的指定员工。

(3) 所有工作细节必须是指定员工的。

其中,(3) 是唯一可以在使用 SQL 引擎时以声明方式实现的。这只是从非核心表到核心表的常规 FK。

(1) 和 (2) 可以在标准 SQL 中以声明方式定义,使用 CREATE ASSERTION 或 CHECK CONSTRAINT 涉及对其他表的引用,而不是定义 CHECK CONSTRAINT 的表,但任何 SQL 引擎都不支持这些构造知道。

关于为什么 [包括] 'type' 列是一个相当糟糕的选择的另一件事:它改变了必须如何制定约束 (3)。例如,您不能再说“所有业务经理都必须是雇员”,而是必须说“所有业务经理都是类型为 <type here> 的雇员”。Iow,您的核心表的“常规 FK”现在已成为对核心表上VIEW的引用,您可能希望将其声明为,例如,

CREATE TABLE BUSMANS ... REFERENCES (SELECT ... FROM CHERE WHERE TYPE='BM');

或者

CREATE VIEW BM AS (SELECT ... FROM CORE WHERE TYPE='BM'); 创建表 BUSMANS ... 参考 BM;

再一次 SQL 不允许你做的事情。

于 2012-06-06T08:23:00.343 回答
1

使用核心表进一步扩展您的一种方法是基于标识列创建代理键。这将为每个员工创建一个唯一的员工 ID(这也将帮助您区分同名的员工)。

外键保留您的参照完整性。您不必像其他人提到的那样需要 EmployeeTypeId,因为您可以过滤 SoftwareDeveloper 或 BusinessManagers 表中的存在。该列将改为充当缓存数据点,以便于查询。

您必须填写以下示例代码中的类型并重命名外键。

create table EmployeeType(
    EmployeeTypeId
,   EmployeeTypeName
,   constraint PK_EmployeeType primary key (EmployeeTypeId)
)

create table Employees(
    EmployeeId int identity(1,1)
,   Name
,   Family
,   EmployeeTypeId
,   constraint PK_Employees primary key (EmployeeId)
,   constraint FK_blahblah foreign key (EmployeeTypeId) references EmployeeType(EmployeeTypeId)
)

create table SoftwareDeveloper(
    EmployeeId
,   Language
,   Technology
,   CanDevelopWeb
,   constraint FK_blahblah foreign key (EmployeeId) references Employees(EmployeeId)
)

create table BusinessManagers(
    EmployeeId
,   FieldOfExpertise
,   MaximumContractValue
,   BonusRate
,   constraint FK_blahblah foreign key (EmployeeId) references Employees(EmployeeId)
)
于 2012-06-05T21:04:51.577 回答
1
  1. 我会添加一个EmployeeIdEmployeeTable
  2. 我会摆脱Type
  3. 例如,对于BusinessManager表格SoftwareDeveloper,我将添加EmployeeId
  4. 然后,您可以从此处继续创建外键,从BusinessManagerSoftwareDeveloperEmployee
于 2012-06-05T20:59:14.030 回答
0

你在正确的轨道上。您可以设置从一般人员表到每个专用表的 PK/FK 关系。您应该将 personID 添加到用于关系的所有表中,因为您不想在 name 上建立关系,因为它不是唯一的 PK,因此它不能是 PK。名称也会更改,对于 FK 关系来说,它们是一个非常糟糕的选择,因为名称更改可能会导致许多记录需要更改。使用单独的表而不是一张表很重要,因为其中一些是一对多的关系。实例的开发人员可能有许多不同的技术,并且这种东西永远不应该存储在逗号分隔的列表中。

您还可以设置触发器以强制只有在主记录具有特定 personType 时才能将记录添加到专业表中。但是,请小心这样做,因为随着时间的推移,您会有人改变角色。您是否想丢失该人在晋升为经理时作为开发人员时所知道的历史。然后,如果他决定退出开发(经常发生),您将不得不重新创建他的旧记录。

于 2012-06-05T21:00:41.197 回答
0

您可以使用同一个表中的所有字段,但是您需要一个名为Employee_Type(例如)的额外表,并且您必须在此处放置Developer, Business Manager,......当然还有一个唯一的 ID。所以你的关系将employee_type_idEmployee table.

employee_type_id使用 PHP 或 ASP,您可以根据下拉菜单中的(或文本)来控制要显示的字段。

于 2012-06-05T20:57:13.207 回答