与不同数据类型的多对多关系

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。

操作顺序如下:

  1. 为客户生成可交付成果时,使用客户 ID 和分组指示符 ('D') 插入 CustGrouping。这会生成此可交付成果的 ID。
  2. 使用生成的 ID,将可交付数据插入 Deliverables table。

相同的操作适用于其他分组。分组 ID 确保该组的 FK 只能引用正确类型的分组。它还可以让您知道哪个 table 包含数据,并且此数据对于每种分组可能完全不同。它是可扩展的,因为要添加一种新的分组类型,将定义插入分组 table,创建一个 table 以包含所需的任何长度和格式的数据,然后从那里开始。

我进一步建议创建视图以显示每种分组的客户数据。例如,视图 CustomerDeliverables 显示具有可交付成果的客户数据。因此,当应用程序的一部分仅处理可交付成果时,它不需要知道如何将其存储在数据库中的详细信息。视图上的触发器可以方便地创建、删除和操作分组数据。