【问题标题】:Many-to-many relationship with different data types不同数据类型的多对多关系
【发布时间】:2017-01-30 17:10:42
【问题描述】:

我们的应用经过定制,可处理多种不同类型的客户,其中某些设置仅适用于少数或一位客户。而不是不断向客户表中添加可为空的列, 我决定添加一个 [Settings] 表以允许每个设置为一行。

[dbo].[Settings]
    [SettingID] [int] 
    [SettingCode] [nchar](4)
    [SettingDescription] [nvarchar](255) 

然后通过多对多表链接到 [Customers] 表

[dbo].[Customer_Settings]
    [Customer_SettingsID] [int]
    [CustomerID] [int] 
    [SettingID] [int] 

我的问题是关于如何处理其中许多设置需要在 [Customer_Settings] 表中添加额外数据类型这一事实。

例如,我们可以有一个设置是需要时间数据类型的“最新交货时间”,或者另一个设置是需要 int 的“直到到期的分钟数”。

我能想到的两种处理方法是将可为空的列添加到 [Customer_Settings] 表中,例如:

[dbo].[Customer_Settings]
    [Customer_SettingsID] [int]
    [CustomerID] [int] 
    [SettingID] [int] 
    [ValueTime] [time] NULL
    [ValueInt] [int] NULL
    ...

这似乎是糟糕的设计。

我能想到的另一种方法是将子表添加到 [Customer_Settings] 表中,例如:

[dbo].[Customer_Settings_Int]
    [Customer_Settings_Int_ID] [int]
    [Customer_SettingsID] [int]
    [Value] [int]

这似乎是规范化的,但也很麻烦。请让我知道其中一个是否明显更好,或者是否有另一种选择。谢谢!

【问题讨论】:

  • 你进入了可怕的实体-属性-值模式。

标签: sql sql-server database-design many-to-many entity-attribute-value


【解决方案1】:

您选择的解决方案称为Entity-Attribute-Value (EAV.) 最常见的方法是将所有值存储为字符串。有些人添加了一个验证器列,其中包含一个正则表达式或类似的表达式,由客户端或 t-sql 函数更新值进行验证。

使用可为空的列要干净得多。

【讨论】:

  • 感谢您澄清/命名我正在处理的概念。 “使用可为空的列要干净得多。”这是推荐第一种方法,其中每个数据类型都是可为空的列?
【解决方案2】:

看起来您可以将属性集收集到逻辑分组中,而不是单例属性列表。您暗示“交付的东西”和“过期的东西”可能是两个这样的分组。在如下所示的查找表中创建这些分组的列表:

ID  Name         Description
 D  Deliverable  Something that is delivered
 E  Expirable    Something that expires

然后为客户和分组创建一种交集表(带有分组类型)

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) 上创建唯一约束?所以它可以作为外键的参考点。

每个分组都需要一个单独的表格。这里只是一个:

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 )
);

检查约束显示如何只能将可交付的数据写入该表。

以下是操作顺序:

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

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

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

【讨论】:

    猜你喜欢
    • 2023-04-10
    • 1970-01-01
    • 2010-12-21
    • 2020-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多