什么是devise模式来支持应用程序中的自定义字段?

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

什么是已知的devise模式来存储字段值和字段的元数据?

我现在看到这些选项:

选项1 :将types为varchar的Field1,Field2,Field3,Field4列添加到我的Customer表中。

选项2 :在customer表中添加一个XMLtypes的列,并将自定义字段的值存储在xml中。

选项3 :添加一个types为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表添加到每个可能值types的列中,并将值存储在右列中。 与#3类似,但字段值是使用强types列存储的。

 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使用特定于一个概念(客户)的表格。 我们的客户也需要其他forms的自定义字段。 我们是否应该有一个全系统的定制现场存储系统? 因此,而不是有多个表,如CustomerCustomFieldValue,EmployeeCustomFieldValue,InvoiceCustomFieldValue,我们将有一个名为CustomFieldValue的表? 虽然看起来对我来说比较优雅,但这不会造成性能瓶颈吗?

你有没有使用这些方法? 你成功了吗? 你会select什么方法? 你知道我应该考虑的其他方法吗?

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

谢谢

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

编辑07-27-2009:

谢谢您的回答。 现在看来,方法列表已经相当全面了。 我已经select了选项2(单个XML列)。 这是目前最容易实施的。 我可能不得不将折射体定义为更加明确的方法,因为我的要求会变得更加复杂,而且支持的自定义字段的数量也会变大。

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

  1. 选项1优点:快速实施。 允许在自定义字段上执行数据库操作(search,sorting)。
    选项1缺点:自定义字段是通用的,所以没有强types的字段。 数据库表是效率低下的,在大小方面有许多无关的字段永远不会被使用。 需要预期的自定义字段的数量。
  2. 选项2优点:快速实施。 灵活,允许任意数量和types的自定义字段。
    选项2缺点:在自定义字段上没有可能的数据库操作。 如果您只需要稍后显示自定义字段,或者仅在每个客户的基础上对数据进行轻微操作,则这是最好的。
  3. 选项3优点:既灵活又高效。 数据库操作可以执行,但是数据有一些规范化以减less浪费的空间。 我同意未知(谷歌)的build议,你添加一个额外的列可以用来指定types或来源信息。 选项3缺点:开发时间和查询复杂度略有增加,但这里确实没有太多的缺点。
  4. 选项4与选项3相同,不同之处在于您的input数据可以在数据库级别上运行。 在选项3中添加types信息到链接表允许您在我们的应用程序级别执行更多的操作,但是数据库将无法进行比较或sorting。 3和4之间的select取决于这个要求。
  5. 选项5与3或4相同,但将解决scheme应用于许多不同表格的灵活性更高。 在这种情况下的成本将是这个表的大小会变得更大。 如果您正在进行许多昂贵的连接操作以访问自定义字段,则此解决scheme可能无法很好地扩展。

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

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

根据下面的评论,你可以用这个模型做出的最重要的错误是把外键放进去。 永远不要把FriendID或TypeID放到这个模型中。 将此模型与典型的关系模型一起使用,并将外键字段保留在表列中。

第二个重大错误是将数据放在这个模型中,需要用每个元素来报告。 例如,在这个模型中join一些用户名就意味着,只要你想访问一个用户,并且需要知道他们的用户名,你最好自己承诺join,或者2n个查询,其中n是你正在查看的用户数量。 当你认为你通常需要每个用户元素的用户名属性的时候,显然这也应该保留在表格列中。

但是,如果你只是用自定义用户字段使用这个模型,你会没事的。 我无法想象很多情况下,用户会input关系数据,并且EAV模型对search没有太大的不利影响。

最后,不要尝试从这个数据join,并获得一个漂亮的logging集。 抓取原始logging,然后抓取该实体的logging集。 如果你发现自己想join表格,你可能会犯上述第二个错误。

如果您使用面向对象的语言进行开发,那么我们在这里讨论自适应对象模型 。 有很多关于如何以oo语言实现它们的文章,而不是关于如何devise数据存储区的信息。

在我工作的公司,我们通过使用关系数据库来存储AOM数据来解决这个问题。 我们有中央实体表,用于显示域中所有不同的“实体”,例如人员,networking设备,公司等等。我们将实际的“表单域”存储到input的数据表中,所以我们有一个表string,date之一等等。 所有的数据表都有一个指向实体表的外键。 我们还需要表格来表示types方面,即某些实体可以具有哪种属性(表单域),这些信息用于解释数据表中的数据。

我们的解决scheme的优点是任何东西都可以在没有代码更改的情况下build模,包括实体,多值之间的引用等等。 也可以将业务规则和validation添加到字段,并且可以以任何forms重用。 缺点是编程模型不容易理解,查询性能会比使用更典型的数据库devise差。 对于AOM来说,除关系数据库以外的其他解决scheme可能会更好,更容易。

build立一个良好的AOM与工作数据存储是很多工作,我不会推荐它,如果你没有高度熟练的开发人员。 也许有一天会有这样的需求的操作系统解决scheme。

自定义字段已经在SO中讨论过了:

  • 你将如何在SQL数据库中创build和存储用户定义的自定义字段?
  • 要添加自定义/用户定义的字段function吗?
  • 我可以使用什么架构来处理购物车,每个产品都需要保存不同的属性

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

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

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

选项4或5将是我的select。 如果你的数据很重要,我不会用Option 3来抛弃你的types信息。(你可能试着自己实现完整的types检查,但这是一个非常大的工作,数据库引擎已经为你做了。

一些想法:

  • 确保你的CustomFields有一个DataType列。
    • CustomFieldValues上使用基于UDF的检查约束来确保由CustomFields.DataType指定的列是非空的。
    • 你还需要一个标准的检查约束来确保你只有一个非null值。
  • 关于外键,我将这些模型作为一个单独的数据DataType
    • 每个潜在的交叉表引用都需要自己的列。 这很好,因为它保持了参照的完整性。
    • 您将不得不在应用程序代码中支持这些关系,因此它们在数据库中被硬编码并不会限制function。
    • 如果您使用的是ORM,这也将与您的ORM保持一致。
  • 对于选项5,使用中间表来build模关系。
    • 您仍然有一个CustomerCustomFieldValue ,而只有CustomerIDCustomFieldValueID列。
  • 仔细想想你的约束每一步。 这是棘手的东西,一个失误可能会导致完全havok下线。

我正在使用这个在当前正在开发的应用程序。 目前还没有任何问题,但EAVdevise仍然吓倒我的日光。 小心一点

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

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

我目前正在处理同样的问题,我select了使用选项3,但是我添加了一个FieldType字段和一个ListSource字段,以防FieldType =“list”。 ListSource字段可以是一个查询,一个sql视图,一个函数名,或者导致列表选项列表的东西。 在我的情况下试图存储这样的字段最大的问题是,这个字段列表可以改变,用户可以稍后编辑数据。 那么,如果字段列表发生了变化,他们将进行编辑。 我对这种情况的解决scheme是,只有在列表没有改变的情况下才允许编辑,如果有的话只显示只读数据。