假设商店产品实体具有共同的特征(例如名称,描述,图像,价格等),并在许多地方参与了逻辑,并且具有(半)独特的特征(例如手表和沙滩球)将由完全不同的方面来描述。因此,我认为EAV是否适合存储那些(半)独特功能?
使用EAV结构具有一些折衷的含义。
您正在为“较少的行空间”进行权衡,因为您没有100个null
与“更复杂的查询和模型”相对的列。
拥有EAV通常意味着该值是一个字符串,可以将任何数据填充到其中。然后,这对有效性和约束检查具有影响。考虑将EAV表中用作电池的电池数量放入电池中的情况。您想找到一个使用C尺寸电池但少于4个的手电筒。
select P.sku
from
products P
attrib Ab on (P.sku = Ab.sku and Ab.key = "batteries")
attrib Ac on (P.sku = Ac.sku and Ac.key = "count")
where
cast(Ac.value as int) < 4
and Ab.value = 'C'
...
在这里要意识到的是,您不能合理地在值上使用索引。您也不能阻止某人在其中放入不是整数或无效整数的东西(使用“ -1”电池),因为value列一次又一次地用于不同目的。
这将对尝试编写产品模型产生影响。您将拥有漂亮的类型化的值...但是您也将Map<String,String>
坐在那里,那里有各种各样的东西。这在将其序列化为XML或Json时具有进一步的含义,以及尝试对那些结构进行验证或查询的复杂性。
要考虑的模式的某些替代方案或修改方案是使用具有有效键的另一个表来代替自由格式键。这意味着您要检查数据库中外键ID的相等性,而不是在数据库中进行字符串比较。更改密钥本身在一处完成。您有一组已知的键,这意味着它们可以作为枚举来完成。
您还可能具有相关的表,其中包含特定产品类别的属性。杂货店部门可以有另一个表,该表具有与建筑材料不需要的多个属性相关的信息(反之亦然)。
+----------+ +--------+ +---------+
|Grocery | |Product | |BuildMat |
|id (fk) +--->|id (pk) |<---+id (fk) |
|expiration| |desc | |material |
|... | |img | |... |
+----------+ |price | +---------+
|... |
+--------+
有时特别需要EAV表。
考虑一下您不仅仅为公司编写库存系统的情况,在公司中您了解每种产品和每种属性。您现在正在编写一个库存系统以出售给其他公司。您无法了解每种产品的每个属性-他们需要定义它们。
出现的一个想法是“我们将让客户修改表”,这很糟糕(您进入表结构的元编程,因为您不再知道什么在哪里,他们可以皇家破坏结构或破坏结构)应用程序,他们就可以执行错误的操作,并且这种访问的含义变得很重要)。MVC4上有更多关于此路径的信息:如何在运行时创建模型?
相反,您可以创建EAV表的管理界面并允许使用该界面。如果客户想为“圆点”创建一个条目,它将进入EAV表,您已经知道如何处理。
在Redmine的数据库模型中可以看到一个示例,您可以看到custom_fields表和custom_values表-这些是允许扩展系统的EAV的一部分。
请注意,如果您发现整个表结构看起来像EAV而不是关系式,则可能要看一下NoSQL的KV风格(cassandra,redis,Mongo等)。意识到这些通常会在设计中进行其他折衷,这些折衷可能与您使用它的目的不同,也可能不合适。但是,它们是针对 EAV结构而专门设计的。
您可能希望阅读库存管理系统的SQL vs NoSQL
按照面向文档的NoSQL数据库(沙发,mongo)的这种方法,您可以将每个清单项目都视为磁盘上的文档...快速提取单个文档中的所有内容。此外,文档结构合理,因此您可以快速拉出任何一件东西。另一方面,在所有文档中搜索与特定属性匹配的事物可能会降低性能(将“ grep”与所有文件进行比较)……这是一个折衷方案。
另一种方法是LDAP,LDAP的所有相关项都将以该基础为基础,但随后还将对其他类型的项应用其他对象类。(请参阅使用LDAP的系统清单)
一旦走上这条路,您可能会发现一些与您要寻找的东西完全匹配的东西……尽管所有事情都有一些折衷。