如何为多种产品设计产品表,其中每种产品都有很多参数

Posted

技术标签:

【中文标题】如何为多种产品设计产品表,其中每种产品都有很多参数【英文标题】:How to design a product table for many kinds of product where each product has many parameters 【发布时间】:2010-10-16 07:05:43 【问题描述】:

我没有太多的桌子设计经验。我的目标是创建一个或多个满足以下要求的产品表:

支持多种产品(电视、电话、PC...)。每种产品都有一组不同的参数,例如:

手机将具有颜色、尺寸、重量、操作系统...

PC 将拥有 CPU、HDD、RAM...

参数集必须是动态的。您可以添加或编辑您喜欢的任何参数。

如果每种产品没有单独的表格,我如何满足这些要求?

【问题讨论】:

有关 EAV 和 JSON 的更多信息:mysql.rjweb.org/doc.php/eav 【参考方案1】:

您可以拥有一个 Product 表和一个单独的 ProductAdditionInfo 表,其中包含 3 列:产品 ID、附加信息名称、附加信息值。如果许多但不是所有类型的产品都使用颜色,则可以将其设置为 Product 表中的可为空列,或者将其放在 ProductAdditionalInfo 中。

这种方法不是关系数据库的传统技术,但我已经看到它在实践中被大量使用。它可以灵活并具有良好的性能。

Steve Yegge 将此称为the Properties pattern,并写了一篇关于使用它的长文。

【讨论】:

属性模式只是实体-属性-值的另一个名称。它被广泛使用,但将其存储在关系数据库中会破坏规范化规则。 老实说,当我阅读@Bills 回答中对 EAV 的描述时,我不太明白他在解释什么。但是当你说3 columns: product ID, additional info name, additional info value时,我理解了这个概念。而且我之前实际上已经这样做过,并且遇到了问题。但是,我现在不记得这些问题是什么。 @JDIsaacks 在这种模式中,一个常见的问题是我们不知道获取所有属性需要多少个 JOIN。【参考方案2】:

您至少有以下五个选项来对您描述的类型层次结构进行建模:

Single Table Inheritance:一张表用于所有产品类型,有足够的列来存储所有类型的所有属性。这意味着 很多 列,其中大部分在任何给定行上都是 NULL。

Class Table Inheritance:一个产品表,存储所有产品类型共有的属性。然后每个产品类型一个表,存储特定于该产品类型的属性。

Concrete Table Inheritance:没有常见产品属性的表。相反,每个产品类型一个表,同时存储常见的产品属性和产品特定的属性。

Serialized LOB:一个产品表,存储所有产品类型共有的属性。一个额外的列以 XML、YAML、JSON 或其他格式存储半结构化数据的 BLOB。此 BLOB 允许您存储特定于每种产品类型的属性。您可以使用花哨的设计模式来描述这一点,例如 Facade 和 Memento。但是,无论您有不能在 SQL 中轻松查询的属性块;您必须将整个 blob 提取回应用程序并在那里对其进行排序。

Entity-Attribute-Value:一张表用于产品,一张表将属性透视到行而不是列。 EAV 就关系范式而言不是一个有效的设计,但无论如何很多人都在使用它。这是另一个答案提到的“属性模式”。请参阅 *** 上的 eav tag 的其他问题,了解其中的一些陷阱。

我在演示文稿中写了更多关于此的内容,Extensible Data Modeling。


关于 EAV 的其他想法:虽然很多人似乎喜欢 EAV,但我不喜欢。它似乎是最灵活的解决方案,因此也是最好的。但是,请记住格言TANSTAAFL。以下是 EAV 的一些缺点:

无法强制列(相当于NOT NULL)。 无法使用 SQL 数据类型来验证条目。 无法确保属性名称的拼写一致。 无法在任何给定属性的值上放置外键,例如查找表。 在传统的表格布局中获取结果既复杂又昂贵,因为要从多行获取属性,您需要为每个属性执行JOIN

EAV 为您提供的灵活性程度需要在其他方面做出牺牲,这可能会使您的代码与以更传统的方式解决原始问题一样复杂(或更糟)。

在大多数情况下,没有必要拥有那种程度的灵活性。在 OP 关于产品类型的问题中,为产品特定属性创建每个产品类型的表要简单得多,因此至少对相同产品类型的条目强制执行一些一致的结构。

只有在必须允许每一行 可能具有一组不同的属性时,我才会使用 EAV。当您拥有一组有限的产品类型时,EAV 是多余的。类表继承将是我的首选。


2019 年更新:我越是看到人们使用 JSON 作为“许多自定义属性”问题的解决方案,我就越不喜欢这种解决方案。它使查询过于复杂,即使使用特殊的JSON functions 来支持它们也是如此。与存储在普通行和列中相比,存储 JSON 文档需要更多的存储空间。

基本上,这些解决方案在关系数据库中都不是简单有效的。具有“可变属性”的整个想法与关系理论根本不一致。

归根结底,您必须选择一种对您的应用程序最不利的解决方案。因此,在选择数据库设计之前,您需要知道如何查询数据。没有办法选择一种“最佳”的解决方案,因为任何解决方案都可能最适合给定的应用程序。

【讨论】:

@HimalayaGarg 选项“4.5”确实与比尔帖子的全部观点相反。 与 MySQL 不同,SQL Server 广泛支持 XML、XPath 和 XQuery。因此,对于 SQL Server 的用户,最好的选择是将额外的属性存储在 XML 类型的列中(选项 4)。这样您就不必“将整个 blob 取回应用程序并在那里对其进行分类”。您甚至可以在 SQL Server 中为 XML 列创建索引。 @Delphi.Boy,很棒的提示! Oracle also supports indexing XML。 IBM DB2 also supports indexing XML. 我更喜欢序列化 LOB。但是它适合ORM吗?我用 EF。 只是一个小更新。如今,mysql 和 postgresql 的最新版本都可以在 JSON 字段中查询数据。【参考方案3】:

如果我使用Class Table Inheritance 含义:

一个产品表,存储所有产品类型共有的属性。然后每个产品类型一个表,存储特定于该产品类型的属性。 -比尔·卡尔文

我最喜欢 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 类与数据库交互以提取产品信息;那么如果代码中的任何更改现在需要重构,它们应该只针对该类。

【讨论】:

【参考方案4】:

@StoneHeart

我会一直使用 EAV 和 MVC。

@比尔·卡文

这里有一些缺点 EAV:

无法强制列(相当于 NOT NULL)。 无法使用 SQL 数据类型来验证条目。 无法确保属性名称的拼写一致。 无法在任何给定属性的值上放置外键,例如 查找表。

你在这里提到的所有那些事情:

数据验证 属性名称拼写验证 必填列/字段 处理依赖属性的销毁

在我看来根本不属于数据库,因为没有一个数据库能够像应用程序的编程语言那样在适当的级别上处理这些交互和要求。

在我看来,以这种方式使用数据库就像用石头敲钉子一样。你可以用一块石头来做,但你不应该使用更精确和专门为这种活动设计的锤子吗?

在传统的表格布局中获取结果很复杂,而且 昂贵,因为要获取属性 从多行你需要做 JOIN 每个属性。

这个问题可以通过对部分数据进行少量查询并使用您的应用程序将它们处理成表格布局来解决。即使您有 600GB 的产品数据,如果您需要此表中每一行的数据,您也可以批量处理。

更进一步如果您想提高查询的性能,您可以选择某些操作,例如 for报告或全局文本搜索并为它们准备索引表,这些表将存储所需数据并定期重新生成,假设每 30 分钟一次。

您甚至不必担心额外数据存储的成本,因为它每天都在变得越来越便宜。

如果您仍然关心应用程序执行操作的性能,您始终可以使用 Erlang、C++、Go 语言来预处理数据,然后在您的主应用程序中进一步处理优化后的数据。

【讨论】:

you can always use Erlang, C++, Go Language to pre-process the data 你是什么意思?而不是 DB,使用 Go 语言?你能详细说明一下吗? 我完全同意。 EAV 是一种可行的方法,特别是如果您需要一定程度的灵活性,这将允许您在不更改数据库架构的情况下添加新的产品和参数,我的意思是通过您的应用程序在生产环境中使用。去过也做过。为我工作。关于慢查询...这里有人听说过缓存吗? ;) @Green 我已经编辑了最后一段以使其更清晰,但它是关于将原始 EAV 数据传递给可以处理数据转换、树结构查找或任何基本的地图减少操作都非常快速并且以一种内存有效的方式。这里的细节将取决于需要优化的内容

以上是关于如何为多种产品设计产品表,其中每种产品都有很多参数的主要内容,如果未能解决你的问题,请参考以下文章

应该如何为具有不同字段的几个猫鼬模型创建模式?

如何处理大维度表

OroCommerce 如何为产品表的新字段查找 Getter 和 Setter

如何为多个产品托管相同的代码?

如何为相同的产品风味拥有多个 ApplicationId?

如何在 Rails 4 中显示购物车中每种产品的材料价格总和?