与不同数据类型的多对多关系
Many-to-many relationship with different data types
我们的应用经过定制以处理多种不同类型的客户,某些设置仅适用于少数或一个客户。而不是不断向客户添加可为空的列 table,
我决定添加一个 [Settings] table 让每个设置成为一行。
[dbo].[Settings]
[SettingID] [int]
[SettingCode] [nchar](4)
[SettingDescription] [nvarchar](255)
然后通过多对多 table
链接到 [客户] table
[dbo].[Customer_Settings]
[Customer_SettingsID] [int]
[CustomerID] [int]
[SettingID] [int]
我的问题是如何处理这样一个事实,即许多这些设置在 [Customer_Settings] table.
上需要额外的数据类型
例如,我们可以让一个设置 "Latest Delivery Time" 需要一个时间数据类型,或者另一个设置 "Minutes Until Expiration" 需要一个 int。
我能想到的两种处理方法是向 [Customer_Settings] table 添加可为空的列,例如:
[dbo].[Customer_Settings]
[Customer_SettingsID] [int]
[CustomerID] [int]
[SettingID] [int]
[ValueTime] [time] NULL
[ValueInt] [int] NULL
...
这似乎是个糟糕的设计。
我能想到的另一种方法是将子 tables 添加到 [Customer_Settings] table,例如:
[dbo].[Customer_Settings_Int]
[Customer_Settings_Int_ID] [int]
[Customer_SettingsID] [int]
[Value] [int]
这看起来很规范,但也很麻烦。如果其中一个明显更好,或者是否有其他选择,请告诉我。谢谢!
您选择的解决方案称为 Entity-Attribute-Value (EAV.) 最常见的方法是将所有值存储为字符串。有些人添加了一个包含正则表达式或类似表达式的验证器列,由客户端或更新值的 t-sql 函数进行验证。
使用可为空的列要干净得多。
您似乎可以将属性集收集到逻辑分组中,而不是单独的属性列表。您暗示 "something that is delivered" 和 "something that expires" 可能是两个这样的分组。在查找 table 中创建这些分组的列表,如下所示:
ID Name Description
D Deliverable Something that is delivered
E Expirable Something that expires
然后为客户和分组(分组类型)创建一种交集table
create table CustGrouping(
CustID int not null references Customer( ID ),
GroupID int auto_generated,
GroupType char( 1 ) not null references Groupings( ID ), -- the table above
constraint PK_CustGrouping primary key( CustID, GroupID ),
constraint UQ_Group_Type unique( GroupID, GroupType )
);
PK 将防止意外地将同一客户与同一分组多次配对。当 GroupID 本身是唯一的时,为什么要在 (GroupID, GroupType) 上创建唯一约束?所以它可以作为外键的参考点。
每个分组都需要一个单独的 table。这里只有一个:
create table Deliverables(
ID int not null primary key,
TypeID char( 1 ) not null check( TypeID = 'D' ),
DeliveryDate date not null,
..., -- all other fields that are associated with deliverables
constraint FK_Deliverables_CustGrouping foreign key( ID, TypeID )
references CustGrouping( GroupID, GroupType )
);
检查约束显示了如何只能将可交付数据写入 table。
操作顺序如下:
- 为客户生成可交付成果时,使用客户 ID 和分组指示符 ('D') 插入 CustGrouping。这会生成此可交付成果的 ID。
- 使用生成的 ID,将可交付数据插入 Deliverables table。
相同的操作适用于其他分组。分组 ID 确保该组的 FK 只能引用正确类型的分组。它还可以让您知道哪个 table 包含数据,并且此数据对于每种分组可能完全不同。它是可扩展的,因为要添加一种新的分组类型,将定义插入分组 table,创建一个 table 以包含所需的任何长度和格式的数据,然后从那里开始。
我进一步建议创建视图以显示每种分组的客户数据。例如,视图 CustomerDeliverables
显示具有可交付成果的客户数据。因此,当应用程序的一部分仅处理可交付成果时,它不需要知道如何将其存储在数据库中的详细信息。视图上的触发器可以方便地创建、删除和操作分组数据。
我们的应用经过定制以处理多种不同类型的客户,某些设置仅适用于少数或一个客户。而不是不断向客户添加可为空的列 table, 我决定添加一个 [Settings] table 让每个设置成为一行。
[dbo].[Settings]
[SettingID] [int]
[SettingCode] [nchar](4)
[SettingDescription] [nvarchar](255)
然后通过多对多 table
链接到 [客户] table[dbo].[Customer_Settings]
[Customer_SettingsID] [int]
[CustomerID] [int]
[SettingID] [int]
我的问题是如何处理这样一个事实,即许多这些设置在 [Customer_Settings] table.
上需要额外的数据类型例如,我们可以让一个设置 "Latest Delivery Time" 需要一个时间数据类型,或者另一个设置 "Minutes Until Expiration" 需要一个 int。
我能想到的两种处理方法是向 [Customer_Settings] table 添加可为空的列,例如:
[dbo].[Customer_Settings]
[Customer_SettingsID] [int]
[CustomerID] [int]
[SettingID] [int]
[ValueTime] [time] NULL
[ValueInt] [int] NULL
...
这似乎是个糟糕的设计。
我能想到的另一种方法是将子 tables 添加到 [Customer_Settings] table,例如:
[dbo].[Customer_Settings_Int]
[Customer_Settings_Int_ID] [int]
[Customer_SettingsID] [int]
[Value] [int]
这看起来很规范,但也很麻烦。如果其中一个明显更好,或者是否有其他选择,请告诉我。谢谢!
您选择的解决方案称为 Entity-Attribute-Value (EAV.) 最常见的方法是将所有值存储为字符串。有些人添加了一个包含正则表达式或类似表达式的验证器列,由客户端或更新值的 t-sql 函数进行验证。
使用可为空的列要干净得多。
您似乎可以将属性集收集到逻辑分组中,而不是单独的属性列表。您暗示 "something that is delivered" 和 "something that expires" 可能是两个这样的分组。在查找 table 中创建这些分组的列表,如下所示:
ID Name Description
D Deliverable Something that is delivered
E Expirable Something that expires
然后为客户和分组(分组类型)创建一种交集table
create table CustGrouping(
CustID int not null references Customer( ID ),
GroupID int auto_generated,
GroupType char( 1 ) not null references Groupings( ID ), -- the table above
constraint PK_CustGrouping primary key( CustID, GroupID ),
constraint UQ_Group_Type unique( GroupID, GroupType )
);
PK 将防止意外地将同一客户与同一分组多次配对。当 GroupID 本身是唯一的时,为什么要在 (GroupID, GroupType) 上创建唯一约束?所以它可以作为外键的参考点。
每个分组都需要一个单独的 table。这里只有一个:
create table Deliverables(
ID int not null primary key,
TypeID char( 1 ) not null check( TypeID = 'D' ),
DeliveryDate date not null,
..., -- all other fields that are associated with deliverables
constraint FK_Deliverables_CustGrouping foreign key( ID, TypeID )
references CustGrouping( GroupID, GroupType )
);
检查约束显示了如何只能将可交付数据写入 table。
操作顺序如下:
- 为客户生成可交付成果时,使用客户 ID 和分组指示符 ('D') 插入 CustGrouping。这会生成此可交付成果的 ID。
- 使用生成的 ID,将可交付数据插入 Deliverables table。
相同的操作适用于其他分组。分组 ID 确保该组的 FK 只能引用正确类型的分组。它还可以让您知道哪个 table 包含数据,并且此数据对于每种分组可能完全不同。它是可扩展的,因为要添加一种新的分组类型,将定义插入分组 table,创建一个 table 以包含所需的任何长度和格式的数据,然后从那里开始。
我进一步建议创建视图以显示每种分组的客户数据。例如,视图 CustomerDeliverables
显示具有可交付成果的客户数据。因此,当应用程序的一部分仅处理可交付成果时,它不需要知道如何将其存储在数据库中的详细信息。视图上的触发器可以方便地创建、删除和操作分组数据。