在 SQLServer 中实现多态关联的最佳方法是什么?

我有吨的实例,我需要实现我的数据库中的某种形式的多态性关联。我总是浪费大量的时间重新考虑所有的选择。以下是我能想到的三种。我希望对于 SQLServer 有一个最佳实践。

下面是多列方法

Multiple Column approach

这里是无外键方法

No Foreign Key Approach

这是基表方法

Base table approach

24243 次浏览

最常见的两种方法是“每类表”(基类表和每个子类表,后者包含描述子类所需的额外列)和“每层表”(一个表中的所有列,再加上一个或多个列,以便对子类进行区分)。哪种方法更好取决于您的应用程序和数据访问策略的具体情况。

在第一个示例中,通过逆转 FK 的方向并从父类中移除额外的 id,您可以得到每个类的表。另外两个基本上是每个类的表的变体。

根据我的看法,你的第一种方法是最好的方法,你可以定义的数据,以及你的类,但作为你的所有主要数据应该为孩子的利用。

因此,您可以检查您的需求并定义数据库。

我使用了我猜你们会称之为基表方法的方法。例如,我有用于姓名、地址和电话号码的表,每个表的标识都是 PK。然后,我有一个主实体表实体(entityID)和一个链接表: tribute (entityKey,AttributeType,AttributeKey) ,其中 AttributeKey 可以指向前三个表中的任何一个,具体取决于 AttributeType。

一些优势: 允许每个实体有尽可能多的名称、地址和电话号码,容易添加新的属性类型,极端规范化,容易挖掘公共属性(即识别重复的人) ,一些其他业务特定的安全优势

缺点: 为了构建简单的结果集而进行的相当复杂的查询使得管理变得困难(例如,我很难雇到有足够好的 T-SQL 技巧的人) ; 对于非常特定的用例而言,性能是最佳的,而不是一般的; 查询优化可能是棘手的

在长得多的职业生涯中,我已经使用这个结构好几年了,如果没有同样奇怪的业务逻辑约束和访问模式,我会犹豫再次使用它。对于一般用法,我强烈建议类型化表直接引用实体。即,实体(entityID)、名称(NameID、 EntityID、 Name)、电话(PhoneID、 EntityID、 Phone)、电子邮件(EmailID、 EntityID、 Email)。您将有一些数据重复和一些公共列,但是编程和优化起来要容易得多。

方法1是最好的,但是某物与 object1,object2,object3之间的关联应该是一对一的。

我的意思是子表(object1,object2,object3)中的 FK 应该是非空唯一键或子表的主键。

Object1、 object2、 object3可以具有多态对象值。

此模型的另一个常见名称是 Supertype 模型,其中有一个基本属性集,可以通过连接到另一个实体来扩展。在 Oracle 的书中,它被作为逻辑模型和物理实现教授。没有关系的模型将允许数据成长为无效状态和孤立记录,我将在选择该模型之前强烈验证需求。存储在基本对象中的关系的顶部模型将导致 null,并且在字段相互排斥的情况下,将始终有一个 null。在子对象中强制使用键的底部关系图将消除 null,但也使依赖项成为一个软依赖项,并允许在不强制级联的情况下使用孤立项。我认为评估这些特征将有助于你选择最适合的模型。我以前用过这三种方法。

没有单一或普遍的最佳实践来实现这一点。这完全取决于应用程序需要的访问类型。

我的建议是概述对这些表的预期访问类型:

  1. 您将使用 OR 层、存储过程还是动态 SQL?
  2. 您期望有多少条记录?
  3. 不同子类之间的差异程度如何? 有多少列?
  4. 您将进行聚合还是其他复杂的报告?
  5. 您是否有用于报告的数据仓库?
  6. 您是否经常需要在一个批处理不同子类的记录? ...

根据这些问题的答案,我们可以找出一个合适的解决办法。

存储特定于子类的属性的另一种可能性是使用具有 Name/value 对的表。如果有大量不同的子类,或者子类中的特定字段很少使用,那么这种方法可能特别有用。

我已经使用了第一种方法。在极端负载下,“某些东西”表成为一个瓶颈。

我采用了为不同对象创建模板 DDL 的方法,并将属性专门化添加到表定义的末尾。

在数据库级别,如果我真的需要表示我的不同类作为一个“某事”记录集,然后我把它们的顶部视图

SELECT "Something" fields FROM object1
UNION ALL
SELECT "Something" fields FROM object2
UNION ALL
SELECT "Something" fields FROM object3

挑战在于如何分配一个非冲突的主键,因为您有三个独立的对象。通常人们使用 UUID/GUID,但在我的例子中,为了避免冲突,密钥是基于时间和机器在应用程序中生成的64位整数。

如果采用这种方法,就可以避免“某些”对象导致锁定/阻塞的问题。

如果你想改变“某些东西”对象,那么现在你有三个独立的对象,这可能会很尴尬,所有这些都需要改变它们的结构。

总结一下。选项一将在大多数情况下工作良好,但在严重的重负荷下,您可能会观察到锁定阻塞,必须拆分出的设计。

具有多列外键的方法1是最好的方法。因为通过这种方式,您可以预先定义与其他表的连接 这使得脚本更容易选择、插入和更新数据。

我用以下方法解决了一个类似的问题:

基于 Many-Many 的设计: 即使 ObjectN 和 Something 之间的关系是1-many,它也等同于通过修改关系表的 PK 而产生的 Many-Many 关系。

首先,我在 ObjectN 和每个 Object 的 Something 之间创建一个关系表,然后使用 Something _ ID 列作为 PK。

这是 Something-Object1关系的 DDL,对 Object2和 Object3也是一样的:

CREATE TABLE Something
(
ID INT PRIMARY KEY,
.....
)


CREATE TABLE Object1
(
ID INT PRIMARY KEY,
.....
)


CREATE TABLE Something_Object1
(
Something_ID INT PRIMARY KEY,
Object1_ID INT NOT NULL,
......


FOREIGN KEY (Something_ID) REFERENCES Something(ID),
FOREIGN KEY (Object1_ID) REFERENCES Object1(ID)
)

更多的细节和例子,其他可能的选项在这张票 同一业务规则的多个外键