sql-server - 您如何在数据库中表示继承?

标签 sql-server inheritance database-design class-table-inheritance

我正在考虑如何在SQL Server数据库中表示复杂的结构。

考虑一个需要存储一系列对象的详细信息的应用程序,这些对象共享一些属性,但还有许多其他不常见的属性。例如,商业保险一揽子计划可能在同一份保单记录中包括责任险,汽车险,财产险和赔偿险。

在C#等中实现此操作很简单,因为您可以创建一个带有Sections集合的Policy,其中Section是根据各种封面类型的要求继承的。但是,关系数据库似乎不允许这样做。

我可以看到有两个主要选择:


创建一个Policy表,然后创建一个Sections表,其中包含所有可能的变体所需的所有字段,其中大多数字段为null。
创建一个Policy表和多个Section表,每种表一个。


这两种选择似乎都不令人满意,特别是因为有必要在所有节中编写查询时,这将涉及大量的联接或大量的空检查。

这种情况下的最佳做法是什么?

最佳答案

在为SQL @Bill Karwin反模式提出解决方案时,SQL Antipatterns在他的Entity-Attribute-Value书中描述了三种继承模型。这是一个简短的概述:

单表继承(又称“表每个层次结构继承”):

如第一种选择那样使用单个表可能是最简单的设计。如您所提到的,许多特定于子类型的属性必须在不适用这些属性的行上赋予NULL值。使用此模型,您将有一个策略表,看起来像这样:

+------+---------------------+----------+----------------+------------------+
| id   | date_issued         | type     | vehicle_reg_no | property_address |
+------+---------------------+----------+----------------+------------------+
|    1 | 2010-08-20 12:00:00 | MOTOR    | 01-A-04004     | NULL             |
|    2 | 2010-08-20 13:00:00 | MOTOR    | 02-B-01010     | NULL             |
|    3 | 2010-08-20 14:00:00 | PROPERTY | NULL           | Oxford Street    |
|    4 | 2010-08-20 15:00:00 | MOTOR    | 03-C-02020     | NULL             |
+------+---------------------+----------+----------------+------------------+

\------ COMMON FIELDS -------/          \----- SUBTYPE SPECIFIC FIELDS -----/


使设计保持简单是一个加号,但是此方法的主要问题如下:


在添加新的子类型时,您必须更改表以容纳描述这些新对象的属性。如果您有许多子类型,或者您打算定期添加子类型,这很快就会成为问题。
数据库将无法执行哪些属性适用,哪些属性不适用,因为没有元数据来定义哪些属性属于哪些子类型。
您也不能对应为必选的子类型的属性实施NOT NULL。您将不得不在您的应用程序中处理此问题,通常这并不理想。


具体表继承:

解决继承的另一种方法是为每个子类型创建一个新表,并重复每个表中的所有公共属性。例如:

--// Table: policies_motor
+------+---------------------+----------------+
| id   | date_issued         | vehicle_reg_no |
+------+---------------------+----------------+
|    1 | 2010-08-20 12:00:00 | 01-A-04004     |
|    2 | 2010-08-20 13:00:00 | 02-B-01010     |
|    3 | 2010-08-20 15:00:00 | 03-C-02020     |
+------+---------------------+----------------+

--// Table: policies_property    
+------+---------------------+------------------+
| id   | date_issued         | property_address |
+------+---------------------+------------------+
|    1 | 2010-08-20 14:00:00 | Oxford Street    |   
+------+---------------------+------------------+


此设计将基本上解决为单表方法确定的问题:


现在可以使用NOT NULL强制使用强制属性。
添加新的子类型需要添加一个新表,而不是在现有表中添加列。
也没有风险为特定的子类型设置了不合适的属性,例如属性策略的vehicle_reg_no字段。
与单表方法一样,不需要type属性。现在,该类型由元数据定义:表名。


但是,此模型还具有一些缺点:


通用属性与特定于子类型的属性混合在一起,并且没有简单的方法来识别它们。数据库也不知道。
定义表时,必须为每个子类型表重复通用属性。绝对不是DRY
无论子类型如何,搜索所有策略都变得很困难,并且需要一堆UNION


无论哪种类型,这都是您必须查询所有策略的方式:

SELECT     date_issued, other_common_fields, 'MOTOR' AS type
FROM       policies_motor
UNION ALL
SELECT     date_issued, other_common_fields, 'PROPERTY' AS type
FROM       policies_property;


请注意,添加新子类型将如何要求对每个子类型使用附加的UNION ALL修改上述查询。如果忘记了此操作,很容易导致应用程序中的错误。

类表继承(又名表每个类型继承):

这是@David mentions in the other answer的解决方案。您为基类创建一个表,其中包含所有公共属性。然后,您将为每个子类型创建特定的表,这些子类型的主键也用作基表的foreign key。例:

CREATE TABLE policies (
   policy_id          int,
   date_issued        datetime,

   -- // other common attributes ...
);

CREATE TABLE policy_motor (
    policy_id         int,
    vehicle_reg_no    varchar(20),

   -- // other attributes specific to motor insurance ...

   FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);

CREATE TABLE policy_property (
    policy_id         int,
    property_address  varchar(20),

   -- // other attributes specific to property insurance ...

   FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);


此解决方案解决了其他两种设计中发现的问题:


强制属性可以使用NOT NULL强制执行。
添加新的子类型需要添加一个新表,而不是在现有表中添加列。
没有为特定的子类型设置不合适的属性的风险。
不需要type属性。
现在,公共属性不再与子类型特定的属性混合。
最后,我们可以保持干燥。创建表时,无需为每个子类型表重复通用属性。
管理策略的自动递增id变得更加容易,因为它可以由基表处理,而不是由每个子类型表独立生成。
现在搜索所有策略,无论其子类型如何都变得非常容易:无需UNION-仅需SELECT * FROM policies


我认为类表方法在大多数情况下是最合适的。



这三个模型的名称来自Martin Fowler'sPatterns of Enterprise Application Architecture

关于sql-server - 您如何在数据库中表示继承?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58961492/

相关文章:

javascript - 在数据库中存储格式化内容的标准方法是什么?

多重继承中的Python self和super

database - 我如何为人群建模?

Javascript 对象继承与 iife

database - 数据库 Fk/Pk 键性能

mysql - 数据库中的一对一关系

sql-server - SQL Server Reporting Services url 参数不起作用

sql-server - tsqlt 错误处理胜过存储过程(单元)错误处理程序

sql - ON运算符SQL,它是什么?

具有相同变量名的 Scala 类继承