如何在 SQL 表中实现这种数据结构
Posted
技术标签:
【中文标题】如何在 SQL 表中实现这种数据结构【英文标题】:How to implement this data structure in SQL tables 【发布时间】:2012-06-05 20:50:25 【问题描述】:我有一个问题可以总结如下:
假设我正在实现一个员工数据库。对于每个人取决于他的职位,应填写不同的字段。因此,例如,如果员工是软件工程师,我有以下列:
Name
Family
Language
Technology
CanDevelopWeb
如果员工是业务经理,我有以下列:
Name
Family
FieldOfExpertise
MaximumContractValue
BonusRate
如果员工是销售人员,那么其他列等等。
如何在数据库架构中实现这一点?
我认为的一种方法是有一些相关的表:
核心表:
Name
Family
Type
如果类型是 1,则员工是软件开发人员,因此其余信息应在表 SoftwareDeveloper
中:
Language
Technology
CanDevelopWeb
对于业务经理,我有另一个包含列的表:
FieldOfExpertise
MaximumContractValue
BonusRate
这种结构的问题是我不确定如何在表之间建立关系,因为一个表与一列上的多个表有关系。
如何强制执行关系完整性?
【问题讨论】:
我可以提供一些反馈吗?你这样做是完全错误的。不要试图从一开始就把它们拆散。对需要的内容进行分类。例如,Manager 继承 Employee,并使用更多字段扩展基本字段。技能集是一个字符串数组(存在表示布尔值)等。不要让它们如此依赖于表格,专注于您拥有的实际需求,以及如何在应用程序中最好地呈现它们。一旦你有了它,我们就可以向你展示如何将它们魔术到表中(EAV 用于 CanDevelopWeb 等功能)和 Manager:Employee 上的继承(通过传播)。 创建基于代码的类,然后将它们转换到数据库。不要,看在皮特的份上,不要先做桌子。制作桌子来存储东西就像我做的最后一件事。毕竟我的 UI、课程、设计、纸和铅笔画,以及其他一切。 How can you represent inheritance in a database?的可能重复 你的问题可能会有答案here 【参考方案1】:这里有一些思想流派。
(1) 将可为空的列存储在单个表中并仅填充相关的列(检查约束可以在此处强制执行完整性)。有些人不喜欢这个,因为他们害怕 NULL。
(2) 您的多表设计,其中每种类型都有自己的表。使用 DRI 更难强制执行,但对于应用程序或触发逻辑可能微不足道。
其中任何一个的唯一问题是,一旦您添加了新属性(如 CanReadUpsideDown
),您必须进行架构更改以适应该问题 - 在 (1) 中,您需要添加一个新列并一个新的约束,在 (2) 中,如果它代表一个新的员工“类型”,则需要添加一个新表。
(3) EAV,其中您有一个存储属性名称和值对的表。您在这里对数据完整性的控制较少,但您当然可以将属性名称限制为某些字符串。我在这里写过这个:
What is so bad about EAV, anyway?【讨论】:
我会在诸如“CanDevelopWeb”之类的东西上使用 EAV,而对诸如姓名/家庭/地址之类的东西使用“更传统的结构”。 @jcolebrand 是的,我并不是要暗示 all 列应该是 EAV,只是那些并非所有员工类型都通用的列。 抱歉,我是在附议您对此的看法。【参考方案2】:您正在描述3 possible strategies 中的一个(“每个表的类”),用于实现类别(也称为继承、泛化、子类)层次结构。
PK 从父表到子表的正确“传播”自然是由它们之间的直接外键强制执行的,但确保子行的存在性和排他性是另一回事。它可以完成(如上面的链接中所述),但增加的复杂性可能不值得,我通常建议在应用程序级别处理它。
【讨论】:
【参考方案3】:-
我会在
EmployeeTable
中添加一个名为EmployeeId
的字段
我会摆脱Type
以BusinessManager
表和SoftwareDeveloper
为例,我将添加EmployeeId
从这里,您可以继续创建从BusinessManager
、SoftwareDeveloper
表到Employee
的外键
【讨论】:
【参考方案4】:进一步扩展核心表的一种方法是根据标识列创建代理键。这将为每个员工创建一个唯一的员工 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)
)
【讨论】:
【参考方案5】:现有的 SQL 引擎没有解决方案可以让您在这种情况下轻松自如。
在“实体子类型化”一章的“数据库管理中的实际问题”中对您的问题进行了相当广泛的讨论。值得称赞的读物,不仅适用于这一章。
从逻辑设计的角度来看,正确的解决方案与您的解决方案类似,但针对核心表中的“类型”列。您不需要那个,因为您可以从员工出现的非核心表中派生“类型”。
您需要查看的是业务规则,也就是数据约束,它将确保数据的整体完整性(也就是一致性)(当然,这些规则是否真正适用是您的业务用户,而不是我,应该告诉你):
每个指定的员工必须有一份工作,因此在某处有一些工作细节。 iow :(1) 没有任何工作详细信息的指定员工和 (2) 没有 >1 工作详细信息的指定员工。
(3) 所有工作详细信息必须是指定员工的。
其中,如果您使用 SQL 引擎,则 (3) 是唯一可以以声明方式实现的。从非核心表到核心表只是一个常规的 FK。
(1) 和 (2) 可以在标准 SQL 中以声明方式定义,使用 CREATE ASSERTION 或 CHECK CONSTRAINT 涉及对其他表的引用,而不是定义 CHECK CONSTRAINT 的表,但这些构造都不受任何支持我知道的 SQL 引擎。
关于为什么 [包括] 'type' 列是一个相当糟糕的选择的另一件事:它改变了必须制定约束 (3) 的方式。例如,您不能再说“所有业务经理都必须是雇员”,而是必须说“所有业务经理都是类型为
CREATE TABLE BUSMANS ... REFERENCES (SELECT ... FROM CORE WHERE TYPE='BM');
或
创建视图 BM AS (SELECT ... FROM CORE WHERE TYPE='BM'); 创建表 BUSMANS ... 参考 BM;
又一次 SQL 不允许你做的事情。
【讨论】:
【参考方案6】:您可以使用同一个表中的所有字段,但是您需要一个名为Employee_Type
的额外表(例如),并且您必须在此处输入Developer, Business Manager,
...当然还有一个唯一的ID。所以你的关系将是employee_type_id
in Employee table
。
使用 php 或 ASP,您可以根据下拉菜单中的 employee_type_id
(或文本)来控制要显示的字段。
【讨论】:
【参考方案7】:你在正确的轨道上。您可以设置从一般人员表到每个专用表的 PK/FK 关系。您应该将 personID 添加到用于关系的所有表中,因为您不想在 name 上建立关系,因为它不能是 PK,因为它不是唯一的。名称也会更改,对于 FK 关系来说,它们是一个非常糟糕的选择,因为名称更改可能会导致许多记录需要更改。使用单独的表而不是一个表很重要,因为其中一些是一对多的关系。实例的开发人员可能有许多不同的技术,并且这种东西永远不应该存储在逗号分隔的列表中。
您还可以设置触发器以强制只有在主记录具有特定 personType 时才能将记录添加到专业表中。但是,请小心这样做,因为您会有人随着时间的推移而改变角色。您是否想丢失当他被提升为经理时,他是一名开发人员时所知道的事情的历史。然后,如果他决定退出开发(经常发生),您将不得不重新创建他的旧记录。
【讨论】:
以上是关于如何在 SQL 表中实现这种数据结构的主要内容,如果未能解决你的问题,请参考以下文章