74

我们开发商业应用程序。我们的客户要求自定义字段支持。例如,他们想向客户表单添加一个字段。

存储字段值和有关字段的元数据的已知设计模式是什么?

我现在看到这些选项:

选项 1:将 varchar 类型的 Field1、Field2、Field3、Field4 列添加到我的 Customer 表中。

选项 2:在客户表中添加一个 XML 类型的列,并将自定义字段的值存储在 xml 中。

选项 3:添加一个带有 varchar 类型列的 CustomerCustomFieldValue 表,并将值存储在该列中。该表还将有一个 CustomerID,一个 CustomFieldID。

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'

CustomFieldID 将是另一个名为 CustomField 的表中的 ID,其中包含以下列:CustomFieldID、FieldName、FieldValueTypeID。

选项 4:添加一个 CustomerCustomFieldValue 表,其中包含每个可能的值类型的列,并将值存储在右列中。与 #3 类似,但字段值使用强类型列存储。

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

选项 5:选项 3 和 4 使用特定于单个概念(客户)的表。我们的客户也要求其他形式的自定义字段。我们是否应该有一个系统范围的自定义字段存储系统?因此,与其拥有多个表,例如 CustomerCustomFieldValue、EmployeeCustomFieldValue、InvoiceCustomFieldValue,我们将拥有一个名为 CustomFieldValue 的表?虽然它对我来说似乎更优雅,但这不会导致性能瓶颈吗?

您是否使用过这些方法中的任何一种?你成功了吗?你会选择什么方法?你知道我应该考虑的其他方法吗?

此外,我的客户希望自定义字段能够引用其他表中的数据。例如,客户可能希望向客户添加“最喜欢的付款方式”字段。付款方式在系统的其他地方定义。这带来了图片中“外键”的主题。我是否应该尝试创建约束以确保存储在自定义字段表中的值是有效值?

谢谢

=======================

编辑 07-27-2009:

谢谢您的回答。看起来方法列表现在非常全面。我选择了选项 2(单个 XML 列)。这是目前最容易实现的。由于我的要求将变得更加复杂,并且要支持的自定义字段的数量将变得更大,因此我可能不得不采用更严格定义的方法。

4

7 回答 7

15

我同意下面的海报,即选项 3、4 或 5 最有可能是合适的。但是,您建议的每个实施都有其好处和成本。我建议通过将其与您的特定要求相匹配来选择一个。例如:

  1. 选项 1 优点:快速实施。允许对自定义字段进行数据库操作(搜索、排序)。
    选项 1 缺点:自定义字段是通用字段,因此没有强类型字段。数据库表效率低下,在大小方面具有许多永远不会使用的无关字段。需要预计允许的自定义字段数量。
  2. 选项 2 优点:快速实施。灵活,允许任意数量和类型的自定义字段。
    选项 2 缺点:无法对自定义字段执行数据库操作。如果您需要做的只是稍后显示自定义字段,或者仅在每个客户的基础上对数据进行少量操作,这是最好的。
  3. 选项 3 优点:既灵活又高效。可以执行 DB 操作,但对数据进行了一定程度的标准化以减少浪费的空间。我同意 unknown (google) 的建议,即您添加一个可用于指定类型或来源信息的附加列。选项 3 缺点:开发时间和查询的复杂性略有增加,但实际上并没有太多缺点。
  4. 选项 4 与选项 3 相同,只是您键入的数据可以在 DB 级别进行操作。在选项 3 中向链接表添加类型信息允许您在我们的应用程序级别执行更多操作,但 DB 将无法进行比较或排序等操作。3 和 4 之间的选择取决于此要求。
  5. 选项 5 与 3 或 4 相同,但更灵活地将解决方案应用于许多不同的表。在这种情况下,成本将是该表的大小将变得更大。如果您正在执行许多昂贵的连接操作来访问您的自定义字段,则此解决方案可能无法很好地扩展。

PS 如下所述,术语“设计模式”通常是指面向对象的编程。您正在寻找数据库设计问题的解决方案,这意味着大多数关于设计模式的建议都不适用。

于 2009-07-14T18:18:13.483 回答
11

就应用程序代码而言,我不确定。我知道自定义字段从数据库中的EAV 模型中受益匪浅。

根据下面的评论,使用此模型可能犯的最严重错误是将外键放入其中。永远不要将 FriendID 或 TypeID 之类的东西放入此模型中。将此模型与典型的关系模型结合使用,并按应有的方式将外键字段保留在表列中。

第二个重大错误是在这个模型中放置了需要与每个元素一起报告的数据。例如,在此模型中放置类似 Username 之类的内容意味着无论何时您想要访问用户并需要知道他们的用户名,您都已承诺加入最多或 2n 个查询,其中 n 是您正在查看的用户数. 当您认为您通常需要每个 User 元素的 Username 属性时,很明显这也应该保留在表列中。

但是,如果您只是将此模型与自定义用户字段一起使用,那您就可以了。我无法想象在很多情况下用户会输入关系数据并且 EAV 模型对搜索并没有太大的不利影响。

最后,不要尝试从这里加入数据并获得漂亮的记录集。抓取原始记录,然后抓取实体的记录集。如果您发现自己很想加入这些表格,那么您可能犯了上面提到的第二个错误。

于 2009-07-14T17:23:38.913 回答
5

如果您正在使用面向对象的语言进行开发,我们在这里讨论的是自适应对象模型。有很多关于如何用 oo 语言实现它们的文章,但没有太多关于如何设计数据存储端的信息。

在我工作的公司,我们通过使用关系数据库存储 AOM 数据解决了这个问题。我们有一个中央实体表,用于呈现域中所有不同的“实体”,如人员、网络设备、公司等……我们将实际的“表单字段”存储到键入的数据表中,因此我们有一个表用于字符串,一个用于日期等等。所有数据表都有一个指向实体表的外键。我们还需要表格来呈现类型方面,即某个实体可以具有什么样的属性(表单字段),这些信息用于解释数据表中的数据。

我们解决方案的优点是任何东西都可以在不更改代码的情况下建模,包括实体之间的引用、多值等等。还可以向字段添加业务规则和验证,并且可以以所有形式重复使用它们。缺点是编程模型不是很容易理解,查询性能会比更典型的数据库设计更差。对于 AOM,关系数据库以外的其他一些解决方案可能会更好、更容易。

为它构建一个具有工作数据存储的良好 AOM 需要大量工作,如果您没有高技能的开发人员,我不会推荐它。也许有一天会有一种操作系统解决方案可以满足这些要求。

自定义字段之前已在 SO 中讨论过:

于 2009-07-14T17:40:50.203 回答
3

像选项 3 这样的方法是要走的路,我以前使用过这种方法。创建一个表来定义其他属性及其相应的值。这将是您的 Customer 和 CustomerCustomField 表(分别)之间的 1-N 关系。关于定义与自定义属性的关系的第二个问题需要考虑。首先想到的是添加一个 DataSource 字段,该字段将包含属性值绑定到的表。所以基本上你的 CustomerCustomField 看起来像:

  1. 客户ID
  2. 财产
  3. 价值
  4. 值数据源(可为空)

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

于 2009-07-14T17:41:04.027 回答
3

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

一些想法:

  • 确保你CustomFields有一个DataType专栏。
    • 使用基于 UDF 的检查约束CustomFieldValues来确保 指定的列CustomFields.DataType不为空。
    • 您还需要一个标准检查约束来确保您只有一个非空值。
  • 关于外键,我会将它们建模为单独的DataType.
    • 每个潜在的交叉表引用都需要自己的列。这很好,因为它保持了参照完整性。
    • 无论如何,您都必须在应用程序代码中支持这些关系,因此它们在数据库中被硬编码这一事实实际上并不会限制功能。
    • 如果您使用的是 ORM,这也将与您的 ORM 配合得很好。
  • 对于选项 5,使用中间表对关系建模。
    • 您仍然会有一个CustomerCustomFieldValue, 但只有CustomerIDCustomFieldValueID列。
  • 每一步都仔细考虑你的限制。这是一件棘手的事情,一个失误可能会导致彻底的破坏。

我在当前正在开发的应用程序中使用它。目前还没有任何问题,但 EAV 设计仍然吓坏了我。小心点。

顺便说一句,XML 也可能是一个不错的选择。我从直接经验中对此了解不多,但它是我在开始数据设计时考虑的选项之一,而且看起来很有希望。

于 2009-07-14T18:00:33.863 回答
0

如果那些“额外”字段是偶然的并且不关心对它们进行搜索,我通常会选择选项 2(但像 JSON 比 XML 更好)。如果要搜索自定义字段,选项 3 并不难做到,通常 SQL 优化器可以从中获得合理的性能。

于 2009-07-14T17:59:20.650 回答
0

我目前正在处理一个有同样问题的项目,我选择使用选项 3,但我添加了一个 FieldType 字段和一个 ListSource 字段以防 FieldType="list"。ListSource 字段可以是查询、sql 视图、函数名称或导致列表选项列表的内容。在我的情况下尝试存储这样的字段的最大问题是该字段列表可以更改,并且允许用户稍后编辑数据。那么如果字段列表发生变化并且他们去编辑该怎么办。我对这种情况的解决方案是仅在列表未更改时才允许编辑,并在已更改时显示只读数据。

于 2017-05-26T19:48:30.857 回答