Answers:
您至少具有以下五个选项来为您描述的类型层次结构建模:
单表继承:所有产品类型的一个表,具有足够的列以存储所有类型的所有属性。这意味着有很多列,其中大多数列在任何给定行上都是NULL。
类表继承:用于产品的一个表,存储所有产品类型共有的属性。然后,每个产品类型一个表,存储特定于该产品类型的属性。
具体表继承:没有用于常见产品属性的表。相反,每种产品类型一个表,既存储通用产品属性又存储特定于产品的属性。
序列化LOB:一个产品表,存储所有产品类型共有的属性。额外的一列以XML,YAML,JSON或其他格式存储了半结构化数据的BLOB。该BLOB允许您存储特定于每种产品类型的属性。您可以使用精美的设计模式对此进行描述,例如Facade和Memento。但是,无论您是否拥有一堆无法在SQL中轻易查询的属性;您必须将整个Blob取回应用程序并在那里进行排序。
Entity-Attribute-Value:一张产品表,一张表将属性转为行而不是列。就关系范式而言,EAV并不是有效的设计,但是无论如何,许多人仍在使用它。这是另一个答案提到的“属性模式”。有关某些陷阱,请参见在StackOverflow上使用eav标签的其他问题。
我在“ 可扩展数据建模”演示文稿中对此进行了详细介绍。
关于EAV的其他想法:尽管许多人似乎都喜欢EAV,但我不赞成。这似乎是最灵活的解决方案,因此也是最好的。但是,请记住格言TANSTAAFL。以下是EAV的一些缺点:
NOT NULL
)。JOIN
对每个属性进行处理。EAV的灵活性程度使您需要在其他方面做出牺牲,这可能使您的代码变得比以更传统的方式解决原始问题要复杂(或更糟)。
在大多数情况下,没有必要具有这种程度的灵活性。在OP有关产品类型的问题中,为每种产品类型的产品特定属性创建一个表要简单得多,因此,至少对于相同产品类型的条目,您必须实施一些一致的结构。
仅在必须允许每一行都可能具有不同的属性集的情况下,才使用EAV 。当您拥有一组有限的产品类型时,EAV会显得过大。类表继承将是我的首选。
2019年更新:我看到人们使用JSON作为解决“许多自定义属性”问题的解决方案的次数越多,我越不喜欢该解决方案。即使使用特殊的JSON函数来支持查询,它也会使查询过于复杂。与存储在普通行和列中相比,存储JSON文档需要更多的存储空间。
基本上,在关系数据库中,这些解决方案都不是简单有效的。具有“可变属性”的整个想法与关系理论根本上是不一致的。
什么它归结为是,你要选择的解决方案,在此基础上是最不坏的一个你的应用程序。因此,在选择数据库设计之前,您需要知道如何查询数据。无法选择一种“最佳”解决方案,因为任何一种解决方案对于给定的应用程序可能都是最佳的。
@铁石心肠
我会一直使用EAV和MVC。
@比尔·卡文
以下是EAV的一些缺点:
- 无法使列成为强制列(等效于NOT NULL)。
- 无法使用SQL数据类型来验证条目。
- 无法确保属性名称拼写一致。
- 无法将外键放在任何给定属性的值上,例如用于查找表。
您在这里提到的所有这些内容:
我认为根本不属于数据库,因为没有一个数据库能够像应用程序的编程语言一样在适当的级别上处理这些交互和需求。
在我看来,以这种方式使用数据库就像用石头砸钉子一样。您可以用岩石来做,但是您不是要使用更精确且专门针对此类活动设计的锤子吗?
传统的表格布局获取结果非常复杂且昂贵,因为要从多个行中获取属性,您需要对每个属性进行JOIN。
可以通过对部分数据进行少量查询并将其处理为应用程序的表格布局来解决此问题。即使您有600GB的产品数据,如果您需要该表中每一行的数据,也可以批量处理。
更进一步如果您想提高查询的性能,则可以选择某些操作,例如报告或全局文本搜索,并为它们准备索引表,这些表将存储所需的数据并定期重新生成,比如说每30分钟一次。
您甚至不必担心额外数据存储的成本,因为它每天变得越来越便宜。
如果您仍然担心应用程序执行的操作的性能,则可以始终使用Erlang,C ++,Go语言对数据进行预处理,之后再在主应用程序中进一步处理优化后的数据。
you can always use Erlang, C++, Go Language to pre-process the data
你什么意思?代替DB,使用Go lang?您能详细说明一下吗?
如果我使用Class Table Inheritance
意思:
一个产品表,存储所有产品类型共有的属性。然后,每个产品类型一个表,存储特定于该产品类型的属性。-比尔·卡温(Bill Karwin)
我最喜欢Bill Karwin的“建议”。我可以预见一个缺点,我将尝试解释如何避免成为问题。
当一种属性仅对1种类型通用,然后对2种,3种通用,等等,我应该制定什么应变计划?
例如:(这只是一个例子,不是我的真实问题)
如果我们出售家具,我们可能会出售椅子,灯具,沙发,电视等。电视可能是我们携带的唯一具有功耗的类型。因此,我会将power_consumption
属性放在上tv_type_table
。但是随后我们开始携带也具有power_consumption
特性的家庭影院系统。好的,它只是另一种产品,所以我也将其添加到该字段中,stereo_type_table
因为这可能是目前最简单的。但是随着时间的流逝,随着我们开始携带越来越多的电子产品,我们意识到power_consumption
它应该足够广泛main_product_table
。我现在应该怎么办?
将字段添加到中main_product_table
。写一个脚本通过电子回路,并从每个就把正确的值type_table
的main_product_table
。然后从每个列中删除该列type_table
。
现在,如果我一直使用相同的GetProductData
类与数据库进行交互以获取产品信息;那么,如果现在需要重构代码中的任何更改,则这些更改应仅适用于该类。
您可以具有一个Product表和一个单独的ProductAdditionInfo表,该表具有3列:产品ID,附加信息名称,附加信息值。如果许多(但不是全部)产品都使用了颜色,则可以将其作为Product表中的可为空列,或者仅将其放入ProductAdditionalInfo中。
这种方法不是关系数据库的传统技术,但是我已经在实践中看到了很多方法。它可以灵活并且具有良好的性能。
3 columns: product ID, additional info name, additional info value
我理解这个概念时。而且我实际上以前已经做过,并且遇到了问题。但是,我现在不记得这些问题是什么。