之前:每个属性一列
ID Ht_cm wt_kg Age_yr ... 1 190 82 43 ... 2 170 60 22 ... 3 205 90 51 ...
之后:所有属性都有一列
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 ...
这个数据库结构有名称吗?有什么相对优势?旧方法似乎更容易将有效性约束置于特定属性(非空,非负等)并且更容易计算平均值.但是我可以看到在不重构数据库的情况下添加新属性会更容易.这是构建数据的标准/首选方式吗?
解决方法
以下是您不应使用EAV的原因列表:
>您不能使用数据类型.如果值是日期,数字或金钱(十进制),则无关紧要.它总是会被强制转换为varchar.这可能是从轻微的表现问题到巨大的痛苦(在月度汇总报告中追逐1美分的差异?).
>您不能(轻松)强制执行约束.它需要一些荒谬的代码才能强制执行“每个人都需要高度在0到3米之间”或“年龄必须不为空且> = 0”,而不是1-2行,每个约束都会处于适当建模的系统中.
>与上述相关,您不能轻易保证您获得每个客户所需的信息(一个人可能缺少年龄,然后下一个可能会丢失他们的身高等).你可以做到这一点,但它比SELECT高度,重量,年龄FROM客户高难度或权重为空是一个很难的地方.
>再次相关,重复数据更难以检测(如果它们为一个客户端提供两个年龄会发生什么?如果你有一个属性翻倍,去除数据,如下所示,将给你两行结果.如果一个客户端有两个单独的两个属性条目,您将从下面的查询中获得四行).
>您甚至无法保证属性名称是一致的. “Age_yr”可能变为“AGE_IN_YEARS”或“age”. (不可否认,当您收到摘录时,与人们插入数据时相比,这不是问题,但仍然如此.)
>任何形式的重要查询都是一场彻底的灾难.为了使三属性EAV系统关系化以便您能够以合理的方式查询它需要EAV表的三个连接.
相比:
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"
至:
SELECT c.ID,c.Ht_cm,c.Wt_kg,c.Age_yr FROM Client c
这是一个(非常简短的)列表,您应该何时使用EAV:
>如果没有办法解决问题,您必须在数据库中支持无模式数据.
>当你只需要存储“东西”而不希望以更结构化的形式需要它时.但要注意,这个怪物叫做“改变要求”.
我知道我只是花了整整一篇文章详细说明为什么在大多数情况下EAV是一个可怕的想法 – 但有一些情况需要/不可避免.然而,在大多数情况下(包括上面的例子),它将比它的价值更麻烦.如果您需要广泛支持EAV类型的数据输入,您应该考虑将它们存储在键值系统中,例如: Hadoop / HBase,CouchDB,MongoDB,Cassandra,BerkeleyDB.