介绍
我一直在阅读有关EAV 数据库的信息,大多数缺点似乎与非常非常糟糕的 EAV 设计或难以从数据生成报告有关。
通常,当您看到人们抱怨 EAV 时,他们使用少于三个表来尝试复制 RDBMS 中单独表 + 列的功能。有时这意味着将从小数到字符串的所有内容存储在单个TEXT
值列中。EAV 还会扰乱数据完整性的保护措施,如果您不小心,这可能会非常糟糕。
但是,EAV 确实提供了一种简单的方法来跟踪历史数据,并允许我们在 SQL 和键值存储系统之间来回移动系统的某些部分。
如果我们根据类型区分不同的实体属性会怎样。除了与特定属性和实体相关的正确索引值之外,这将允许我们仍然处理 belongsTo、Has、HasMany 和 HasManyThrough 关系。
考虑以下两个基本实体
products (price -> decimal, title -> string, desc -> text, etc...)
attributes
options
[...]
int
datetime
string
text
decimal
relation
[id,foreign_key]
users (gender -> options, age -> int, username -> string, etc...)
attributes
options
[...]
int
datetime
string
text
decimal
relation
[id,foreign_key]
RDBMS 模式设计
众所周知,用户资料和产品是世界上最多样化的项目之一。每个公司对它们的处理方式不同,并且针对它们的需要有不同的“列”或“属性”。
以下是如何处理多个(嵌套和/或关系)实体的视图。
这个想法是,每个实体都有这个主属性表,然后指定如何查找和解释这些值。这使我们能够处理特殊情况,例如其他实体的外键以及“选项”或十进制数之类的东西。
entity_type { id, type, // 即“博客”、“用户”、“产品”等。 created_at }
entity {
id,
entity_type_id,
created_at
}
attr {
id,
entity_id,
type,
name,
created_at
}
option {
id,
attr_id,
entity_id,
multiple, // multiple values allowed?
name,
created_at
}
attr_option {
id
attr_id,
entity_id,
option_id
option,
created_at
}
attr_int {
attr_id,
entity_id,
int,
created_at
}
attr_relation {
attr_id,
entity_id,
entity_fk_id,
created_at
}
attr_datetime {
attr_id,
entity_id,
datetime,
created_at
}
attr_string {
attr_id,
entity_id,
var_char,
created_at
}
attr_text {
attr_id,
entity_id,
text,
created_at
}
attr_decimal {
attr_id,
entity_id,
decimal,
created_at
}
像这样的表将允许我们永远不必这样做,UPDATE ...
因为我们可以只INSERT INTO ...
为每个更改值的新属性添加created_at
以了解最新的值是什么。这非常适合保存历史数据的记录(当然仍然可以例外)。
示例查询
首先,它是什么“类型”的实体?(用户、帖子、评论等)
SELECT * FROM entity_type et LEFT JOIN entity e ON e.entity_type_id = et.id WHERE e.id = ?
接下来,这个实体的属性是什么?(表属性)
SELECT * FROM attr WHERE entity_id = ?
接下来,该实体的属性中存在哪些值?(attr_### 表)
SELECT * FROM attr_option, attr_int, attr_relation, attr_text, ... WHERE entity_id = ?
vs
SELECT * FROM attr_option WHERE entity_id = ? if( ! multiple) ORDER BY created_at DESC LIMIT 1
SELECT * FROM attr_int WHERE entity_id = ? ORDER BY created_at DESC LIMIT 1
SELECT * FROM attr_relation WHERE entity_id = ? ORDER BY created_at DESC LIMIT 1
SELECT * FROM attr_text WHERE entity_id = ? ORDER BY created_at DESC LIMIT 1
...
该实体存在哪些关系?
假设我们有一个 ID 为 34 的“post”实体,并且我们想要它的“comments”(entity_type = 2),这可以让我们获取产品实体上的评论实体 ID:
SELECT * FROM entity AS e
LEFT JOIN attr_relation AS ar ON ar.entity_id = e.id
WHERE ar.entity_id = 34 AND e.entity_type = 2;
除了多个查询(无论如何都需要键值存储),这种方法会存在什么问题?