pro*_*ype 70 schema database-design ontology eav
我们处理来自客户的常规数据馈送,该客户刚刚将其数据库从一种看起来很熟悉的表单(每个实体一行,每个属性一列)重构为一个我似乎不熟悉的表单(每个实体每个属性一行):
之前:每个属性一列
ID Ht_cm wt_kg Age_yr ...
1 190 82 43 ...
2 170 60 22 ...
3 205 90 51 ...
Run Code Online (Sandbox Code Playgroud)
之后:所有属性的一列
ID Metric Value
1 Ht_cm 190
1 Wt_kg 82
1 Age_yr 43
1 ...
2 Ht_cm 170
2 Wt_kg 60
2 Age_yr 22
2 ...
3 Ht_cm 205
3 Wt_kg 90
3 Age_yr 51
3 ...
Run Code Online (Sandbox Code Playgroud)
这个数据库结构有名字吗?有哪些相对优势?旧方法似乎更容易对特定属性(非空、非负等)设置有效性约束,并且更容易计算平均值。但是我可以看到在不重构数据库的情况下添加新属性可能会更容易。这是构建数据的标准/首选方式吗?
Sim*_*rts 96
它被称为实体-属性-值(有时也称为“名称-值对”),这是人们在关系数据库中使用 EAV 模式时“方孔中的圆钉”的经典案例。
以下是不应该使用 EAV的原因列表:
SELECT height, weight, age FROM Client where height is null or weight is null
.相比:
SELECT cID.ID AS [ID], cH.Value AS [Height], cW.Value AS [Weight], cA.Value AS [Age]
FROM (SELECT DISTINCT ID FROM Client) cID
LEFT OUTER JOIN
Client cW ON cID.ID = cW.ID AND cW.Metric = "Wt_kg"
LEFT OUTER JOIN
Client cH ON cID.ID = cH.ID AND cW.Metric = "Ht_cm"
LEFT OUTER JOIN
Client cA ON cID.ID = cA.ID AND cW.Metric = "Age_yr"
Run Code Online (Sandbox Code Playgroud)
到:
SELECT c.ID, c.Ht_cm, c.Wt_kg, c.Age_yr
FROM Client c
Run Code Online (Sandbox Code Playgroud)
这是您应该何时使用 EAV 的(非常短的)列表:
我知道我只是用整篇文章详细说明了为什么在大多数情况下 EAV 是一个糟糕的主意 - 但也有一些需要/不可避免的情况。然而,大多数时候(包括上面的例子),它会比它的价值要麻烦得多。如果您需要广泛支持 EAV 类型的数据输入,您应该考虑将它们存储在键值系统中,例如 Hadoop/HBase、CouchDB、MongoDB、Cassandra、BerkeleyDB。
Erw*_*ter 16
在 PostgreSQL 中,处理 EAV 结构的一种很好的方法是附加模块hstore
,可用于 8.4 或更高版本。手册:
该模块实现了
hstore
用于在单个 PostgreSQL 值中存储键/值对集的数据类型。这在各种情况下都很有用,例如具有许多很少检查的属性的行或半结构化数据。键和值只是文本字符串。
需要额外的模块 hstore。看:
从 Postgres 9.2 开始,它还有json
类型和大量的功能(其中大部分是在 9.3 中添加的)。
Postgres 9.4 添加了(很大程度上优越的)“二进制 JSON”数据类型jsonb
。具有高级索引选项。
Mel*_*YRE 11
看到 EAV db 模型如何受到批评,甚至被某些人视为“反模式”,这很有趣。
就我而言,主要缺点是:
但是,您绝对不应该放弃此解决方案,原因如下:
Tar*_*ryn 10
如果您有一个使用 EAV 结构的数据库,则可以通过多种方式查询数据。
@Simon 的回答已经展示了如何使用多个连接执行查询。
使用的样本数据:
CREATE TABLE yourtable ([ID] int, [Metric] varchar(6), [Value] int);
INSERT INTO yourtable ([ID], [Metric], [Value])
VALUES (1, 'Ht_cm', 190),
(1, 'Wt_kg', 82),
(1, 'Age_yr', 43),
(2, 'Ht_cm', 170),
(2, 'Wt_kg', 60),
(2, 'Age_yr', 22),
(3, 'Ht_cm', 205),
(3, 'Wt_kg', 90),
(3, 'Age_yr', 51);
Run Code Online (Sandbox Code Playgroud)
如果您使用的是具有PIVOT
函数的 RDBMS (SQL Server 2005+ / Oracle 11g+),那么您可以通过以下方式查询数据:
select id, Ht_cm, Wt_kg, Age_yr
from
(
select id, metric, value
from yourtable
) src
pivot
(
max(value)
for metric in (Ht_cm, Wt_kg, Age_yr)
) piv;
Run Code Online (Sandbox Code Playgroud)
如果您无权访问PIVOT
函数,则可以使用带有CASE
语句的聚合函数来返回数据:
select id,
max(case when metric ='Ht_cm' then value else null end) Ht_cm,
max(case when metric ='Wt_kg' then value else null end) Wt_kg,
max(case when metric ='Age_yr' then value else null end) Age_yr
from yourtable
group by id
Run Code Online (Sandbox Code Playgroud)
这两个查询都会在结果中返回数据:
| ID | HT_CM | WT_KG | AGE_YR |
-------------------------------
| 1 | 190 | 82 | 43 |
| 2 | 170 | 60 | 22 |
| 3 | 205 | 90 | 51 |
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
13000 次 |
最近记录: |