Database design p在客户和CustomerCustomField表之间(分别)。关于定义与自定义属性的关系的第二个问题需要考虑。首先想到的是添加一个DataSource字段,该字段将包含属性值绑定到的表。因此,基本上,您的CustomerCustomField看起来像: 客户ID 财产 价值观 ValueDataSource(可为空)

Database design p在客户和CustomerCustomField表之间(分别)。关于定义与自定义属性的关系的第二个问题需要考虑。首先想到的是添加一个DataSource字段,该字段将包含属性值绑定到的表。因此,基本上,您的CustomerCustomField看起来像: 客户ID 财产 价值观 ValueDataSource(可为空),database-design,data-modeling,Database Design,Data Modeling,这应该允许您绑定到特定的数据结构,或者只允许您指定未绑定的值。您可以进一步规范化这个模型,但是类似的东西可以工作,并且应该很容易在代码中处理 如果这些“额外”字段是偶然出现的,并且不想对它们进行搜索,我通常会选择选项2(但更喜欢JSON而不是XML)。如果要在自定义字段上进行搜索,选项3并不难,通常SQL优化器可以从中获得合理的性能。选项4或5将是我的选择。如果您的数据很重要,我不会用选项3丢弃您的类型信息。(您可以尝试自己实现完整类型检查,但这是一项相当大的工作,数据库引擎已经为您完成了这项工

这应该允许您绑定到特定的数据结构,或者只允许您指定未绑定的值。您可以进一步规范化这个模型,但是类似的东西可以工作,并且应该很容易在代码中处理

如果这些“额外”字段是偶然出现的,并且不想对它们进行搜索,我通常会选择选项2(但更喜欢JSON而不是XML)。如果要在自定义字段上进行搜索,选项3并不难,通常SQL优化器可以从中获得合理的性能。

选项4或5将是我的选择。如果您的数据很重要,我不会用选项3丢弃您的类型信息。(您可以尝试自己实现完整类型检查,但这是一项相当大的工作,数据库引擎已经为您完成了这项工作。)

一些想法:

  • 确保
    CustomFields
    具有
    DataType
    列。
    • CustomFieldValues
      使用基于UDF的检查约束,以确保
      CustomFields.DataType
      指定的列不为空
    • 您还需要一个标准的检查约束,以确保只有一个非空值
  • 关于外键,我将把它们建模为一个单独的
    数据类型
    • 每个潜在的交叉表引用都需要自己的列。这很好,因为它保持了引用完整性
    • 无论如何,您都必须在应用程序代码中支持这些关系,因此它们在数据库中硬编码的事实实际上并不限制功能
    • 如果您使用的是ORM,这也会很好地与您的ORM配合
  • 对于选项5,使用中间表对关系进行建模。
    • 您仍然会有一个
      CustomerCustomFieldValue
      ,但是只有
      CustomerID
      CustomFieldValueID
  • 每一步都要仔细考虑你的限制。这是一件棘手的事情,一个失误就可能导致彻底的失败
我正在开发的应用程序中使用它。虽然还没有出现任何问题,但EAV的设计仍然让我胆战心惊。小心点


另外,XML可能也是一个不错的选择。从直接的经验来看,我不太了解这一点,但这是我在开始数据设计时考虑的选项之一,看起来很有希望。

我同意下面的海报,选项3、4或5最有可能是合适的。但是,您建议的每个实现都有其好处和成本。我建议根据您的具体要求选择一个。例如:

  • 选项1优点:快速实施。允许对自定义字段执行DB操作(搜索、排序)。
    选项1缺点:自定义字段是泛型的,因此没有强类型字段。数据库表是低效的、按大小排列的,有许多永远不会使用的无关字段。需要预计允许的自定义字段数
  • 选项2优点:快速实施。灵活,允许任意数量和类型的自定义字段。
    选项2缺点:在自定义字段上不可能执行DB操作。如果您只需要稍后显示自定义字段,或者只对每个客户的数据进行较小的操作,那么这是最好的
  • 选项3优点:既灵活又高效。可以执行DB操作,但数据在某种程度上被规范化,以减少浪费的空间。我同意unknown(google)的建议,您可以添加一个额外的列,用于指定类型或源信息。 选项3的缺点:在开发时间和查询的复杂性上略有增加,但这里确实没有太多的缺点
  • 选项4与选项3相同,只是键入的数据可以在DB级别进行操作。在选项3中,向链接表中添加类型信息允许您在应用程序级别执行更多操作,但例如,DB将无法进行比较或排序。3和4之间的选择取决于此要求
  • 选项5与3或4相同,但更灵活地将解决方案应用于许多不同的表。在这种情况下,成本将是该表的大小将变得更大。如果要执行许多昂贵的联接操作来访问自定义字段,则此解决方案可能无法很好地扩展
    注:如下所述,“设计模式”一词通常指的是面向对象编程。您正在寻找一个数据库设计问题的解决方案,这意味着大多数关于设计模式的建议都不适用。

    我目前正在处理一个有同样问题的项目,我选择使用选项3,但我添加了一个FieldType字段和一个ListSource字段以防FieldType=“list”。ListSource字段可以是查询、sql视图、函数名,也可以是生成列表选项列表的内容。在我的情况下,尝试存储这样的字段的最大问题是该字段列表可能会更改,并且允许用户稍后编辑数据。因此,如果字段列表已更改,并且它们进入编辑状态,该怎么办。我的解决方案是,仅当列表未更改时才允许编辑,如果列表已更改,则显示只读数据。

    选项2的扩展是二进制序列化,而不是XML。当你问到有什么设计模式时——Martin Fowler在他的《企业应用程序架构模式》一书中称之为序列化LOB——请看我是curi
    CustomerID,  CustomFieldID, Value
    10001,       1001,          '02/12/2009 8:00 AM'
    10001,       1002,          '18.26'
    10002,       1001,          '01/12/2009 8:00 AM'
    10002,       1002,          '50.26'
    
    CustomerID,  CustomFieldID, DateValue,           StringValue,       NumericValue                 
    10001,       1001,          02/12/2009 8:00 AM,  null,              null
    10001,       1002,          null,                null,              18.26
    10002,       1001,          01/12/2009 8:00 AM,  null,              null
    10002,       1002,          null,                null,              50.26