在应用程序中支持自定义字段的设计模式是什么?


72

我们开发了一个商业应用程序。我们的客户要求定制领域的支持。例如,他们想在“客户”表单中添加一个字段。

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

我现在看到这些选项:

选项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列)。目前,这是最容易实现的。由于我的要求将变得更加复杂,并且要支持的自定义字段的数量将变得更大,因此我可能不得不采用更严格定义的方法。


选项2的扩展是二进制序列化而不是XML。当您问到那里有哪些设计模式时-马丁·福勒(Martin Fowler)在他的《企业应用程序体系结构的模式》一书中将此称为序列化
LOB-

我对您选择的方法的结果感到好奇:您是否能够使用XML(或序列化的LOB)对自定义字段执行排序/搜索/过滤?这是否足以满足您的数据存储需求,还是您不得不采取其他方法?如果您能分享知识,那就太好了。
user1987392

您没有描述消费者如何使用这些数据,这是选择设计解决方案的重要因素。
jangorecki

最终这如何为您解决?我发现自己面临同样的问题,网络上的信息充其量似乎很少。
Joseph Devlin

1
最简单的选项(每个表中的XML列)结果很好。7年后,我们将改变设计。我们将进行运行时模式修改。当客户添加字段时,我们会在数据库中即时创建一个新列,并使用正确的数据类型和其他表的适当外键。发生此更改的主要原因:类型(数据完整性),外键(数据完整性),性能(索引),清晰度(查询和迁移脚本更易于编写)。XML易于实现并且为我们提供了良好的服务。这是一个可行的选择。
西尔万

Answers:


15

我确实同意以下海报,认为选项3、4或5最适合。但是,您建议的每个实现都有其收益和成本。我建议您通过将其与您的特定需求匹配来选择它。例如:

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

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


我接受此答案,因为我认为这有助于选择解决方案。
西尔万

关于选项3,“(...)但是这里确实没有太多缺点。” →并没有试图从您的答案中删除任何可信度,但#3/4/5几乎是Entity-Value-Attribute模式,有人认为这是一种反模式,并且围绕它的性能有很多“恐怖故事”网络。另一方面,许多人说可以使用XML / BLOB进行搜索/排序/过滤(@服务器端),但是我从未见过这样做...我正在摸索哪种方法(EAV或XML)有效最好,尤其是在使用ORM时。
user1987392

10

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

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

第二个重大错误是在该模型中放置了需要与每个元素一起报告的数据。例如,在此模型中放置类似“用户名”之类的东西意味着,只要您想访问用户并且需要知道其用户名,就可以将自己最多进行一次联接或2n次查询,其中n是您正在寻找的用户数。当您认为通常每个用户元素都需要Username属性时,很明显,这也应该保留在表列中。

但是,如果仅将此模型与自定义用户字段一起使用,则可以。我无法想象在许多情况下用户会输入相关数据,而EAV模型不会对搜索造成太大影响。

最后,不要尝试从中加入数据并获得漂亮的记录集。抓取原始记录,然后获取该实体的记录集。如果您发现自己想加入表格,则可能是您犯了上述第二个错误。


我正在我的当前项目中使用此模型,这确实非常有益。您还可以在必要时为简单查询/数据绑定生成数据的非规范化视图。
Dillie-O

是的,如果仔细应用,这些功能将非常强大。
斯潘塞·鲁波特

1
请密切注意Wikipedia文章中的警告。做得不好,特权/价值对会破坏一个系统,正如散布在网络上的许多灾难故事所证明的那样。如果用户定义的数据不仅要与“直接父级”相关,还需要更多的项目,那么最好将数据作为新的行或表添加到模型中。我的主要建议是非常努力不要过度使用这样的系统。
菲利普·凯利2009年

在Wikipedia上有趣的阅读。EAV模型肯定是一把双刃剑。我的+1为这种方法起了个名字,而维基百科的文章相当不错。就个人而言,使用此方法可以看到很多令人沮丧的地方,但是可以看到它在哪些方面有用。尽管维基百科文章指出,但我认为拥有XML列是一种严肃的选择。
RichardOD

我认为每个人都围绕这个话题提出了好意见。对我来说:XML只是数据库中行的另一种表示形式。因此,添加XML列就像在行中有一个小型数据库。:(
Diederik 2012年

5

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

在我工作的公司中,我们已经使用关系数据库存储AOM数据解决了该问题。我们具有中央实体表,用于显示域中的所有不同“实体”,例如人员,网络设备,公司等。我们将实际的“表单字段”存储到键入的数据表中,因此我们有一个表用于字符串,一个用于日期等等。所有数据表都有一个指向实体表的外键。我们还需要表来表示类型侧,即某些实体可以具有哪种属性(表单字段),并且此信息用于解释数据表中的数据。

我们解决方案的优点在于,无需更改代码即可对任何事物建模,包括实体之间的引用,多值等。也可以将业务规则和验证添加到字段中,并且可以以所有形式重复使用它们。缺点是编程模型不是很容易理解,查询性能将比更典型的数据库设计差。对于AOM,关系数据库以外的其他解决方案可能会更好,更轻松。

为此,使用有效的数据存储来构建良好的AOM需要大量工作,如果您没有高技能的开发人员,我不建议您这样做。也许有一天将有针对这些需求的OS解决方案。

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


3

诸如选项3之类的方法可以解决,我以前使用过这种方法。创建一个表来定义其他属性及其对应的值。这将分别是您的Customer和CustomerCustomField表之间的1-N关系。关于定义与自定义属性的关系的第二个问题将引起我们的思考。首先想到的是添加一个DataSource字段,该字段将包含属性值绑定到的表。因此,您的CustomerCustomField本质上看起来像:

  1. 顾客ID
  2. 属性
  3. ValueDataSource(可为空)

这应该允许您绑定到特定的数据结构,或者仅允许您指定未绑定的值。您可以进一步规范化此模型,但是类似的方法可能会起作用,并且应该足够容易在代码中进行处理。


3

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

一些想法:

  • 确保您CustomFields有一DataType列。
    • 使用基于UDF的检查约束,CustomFieldValues以确保指定的CustomFields.DataType列为非空。
    • 您还需要一个标准的检查约束,以确保您恰好有一个非空值。
  • 关于外键,我会将它们建模为单独的DataType
    • 每个潜在的交叉表引用都将需要其自己的列。这很好,因为它保持了引用完整性。
    • 无论如何,您都必须在应用程序代码中支持这些关系,因此将它们硬编码在数据库中这一事实实际上并没有限制功能。
    • 如果您使用的是ORM,这也可以很好地配合您的ORM。
  • 对于选项5,使用中介表对关系进行建模。
    • 您仍然会有一个CustomerCustomFieldValue,但只有CustomerIDandCustomFieldValueID列。
  • 在每一步中都要认真思考您的约束。这是一件棘手的事情,一个失误会导致一路狂奔。

我正在当前正在开发的应用程序中使用它。尚未出现任何问题,但EAV设计仍然使我感到恐惧。请注意。

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


0

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


0

我目前正在处理具有相同问题的项目,并且我选择使用选项3,但是我添加了一个FieldType字段和一个ListSource字段,以防FieldType =“ list”。ListSource字段可以是查询,SQL视图,函数名称或导致列表选项列表的内容。在我的情况下,尝试存储这样的字段的最大问题是此字段列表可以更改,并且允许用户以后编辑数据。因此,如果字段列表已更改并且要去编辑该怎么办。对于这种情况,我的解决方案是只允许在列表未更改的情况下进行编辑,而在列表已更改的情况下显示只读数据。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.