Entity–Attribute–Value table (EAV table) 是否需要单独的 ID 字段作为主键或复合主键(如 entityId+attributeId)?

Does an Entity–Attribute–Value table (EAV table) need a separate ID field as primary key or composite primary key (like entityId+attributeId)?

这是我的 EAV table 结构(我知道 EAV 不好,但我需要存储的属性数量超过一万,所以规范化的 table 不起作用)

Table name - propertyAssign

entityId - int - indexed
attributeId - smallint - composite index with valueId
valueId - smallint - composite index with attributeId

我只需要通过两种方式查询这个table

  1. select attributeId,来自 propertyAssign 的 valudId,其中 entityId=x
  2. select 来自 propertyAssign 的 entityId,其中 attributeId=x 且 valueId=y

所以我相应地添加了索引。

问题 => 我需要在这里添加任何主键吗?

已编辑:

考虑到您必须执行的查询,我认为最好的做法是:

  1. 删除超过 entityId 的索引;
  2. 声明所有三个属性为主键(按顺序 entityId, attributeId, valueId,不引入显式(代理)主键。

主键声明将导致在 entityId, attributeId, valueId 上创建复合索引。

这有两个作用:

  1. 可以减少由于缺少主键而导致的问题(有关问题的示例,请参见 this)和
  2. 它将加速第一种类型的查询 (select attributeId, valudId from propertyAssign where entityId=x),因为访问计划可以仅使用索引来给出查询结果。

Renzo 的回答包括 Select #1,但不包括

select entityId from propertyAssign where attributeId=x and valueId=y

那需要

INDEX(attributeId, valueId, entityId)

会是

  • 高效,因为它完美地处理了 WHERE 子句,并且
  • 效率更高,因为 INDEX 包含所有需要的字段 ("Covering index")。

是的,这基本上使 table 的大小翻了一番(数据+PK,然后是包含所有数据的索引)。但这比 table 扫描 Select #2 好

闻起来像 attributeIdvalueId 是指向 'normalization' table 的链接,它们具有实际的字符串和值??完成代码所需的 JOIN 在哪里?如果您在单独的 SELECT 中执行此操作,则效率低于 JOIN,因为它是到服务器的两次(或三次?)往返。

EAV 是一个非常糟糕的设计模式;祝你好运。

编辑

提到的两个SELECTs将受益于这两个索引:

 INDEX(entityId, attributeId, valueId) -- for Select #1
 INDEX(attributeId, valueId, entityId) -- for Select #2

而且,由于该三元组是唯一的,因此其中一个 INDEX 也可能是 PRIMARY KEY。现在,选择哪个...

INSERTing 时,PK 以 entityId 开头使得一个实体的所有三元组都 'clustered' 在一起。这加快了 INSERTSELECT #1。所以我投票支持它作为PK。让另一个PK不会加速INSERTs。这是因为创建具有大量属性的实体会导致大量分散写入。

两个 SELECTs 中的每一个都由一个或另一个索引最佳处理;所以 SELECTs 尽可能快。好吧,我忽略了您规范化属性名称和值这一事实。稍后我会咬你,并提出更丑陋的问题。

我说这是一个糟糕的设计,部分原因是因为一个非常相似的模式的基准。压力测试在 table 中填充了超过可缓存的内容。插入速率不能超过每秒 7 个实体。那是在 RAID 条带化磁盘 运行 满容量的情况下。规范化属性等导致 lots 随机磁盘命中。