实体属性值(EAV)的替代方案是什么?

54

我们的数据库基于EAV(实体-属性-值)模型设计。 了解EAV模型的人都知道为了获得灵活性而带来的所有问题。

我问我的客户为什么使用EAV模型(灵活性),他们的回答是:他们的实体随时间变化。 因此,今天他们可能拥有一个带有一些属性的表,但在一个月的时间内,可能会添加一些新属性,或者重命名现有属性。 他们需要生成报告,以便在任何时间段内返回并根据该阶段实体的形状查询数据。

我理解这在传统的关系型模型中是不可行的,但我个人认为EAV是反模式。 是否有其他替代模型能够捕获实体和实例中更改的时间维度?

干杯, Mosh


2
不要替换你已经拥有的,因为它确实满足了特定的需求,你应该考虑使用一些能够存储随时间变化的数据的东西来增强你的基本EAV模型。 - RibaldEddie
我同意RibaldEddie的观点,这并不简单,但是在您的属性定义中添加日期/版本可能比完全重构基于当前模式构建的所有代码更容易。 - JeremyWeir
5个回答

54

EAV实现得好与坏之间存在巨大区别;由熟练人员实现的5NF与由一无所知者实现的5NF也有所不同。

第六范式是不可约范式(无法进一步规范化),它消除了许多常见问题,如空值问题,并提供了识别缺失值的最终方法。它是学术和技术上强大的范式。没有产品支持它,也不常用。要正确而一致地实现它,需要实施元数据目录。当然,用于导航它所需的SQL会变得更加繁琐(SQL已经很复杂了),但这可以通过从元数据自动产生SQL来轻松解决。

EAV是6NF的部分集或子集。问题在于,通常它是为某个目的(允许添加列而不必进行DDL更改)而进行的,由不了解6NF且不实现元数据的人实施。关键是,6NF和EAV作为原则和概念提供了实质性的好处和性能增益;但通常情况下,它们没有得到正确实施,因此好处无法得以实现。相当多的EAV实现都非常糟糕,这并不是因为EAV本身不好,而是因为实现很差。

例如,有些人认为从6NF/EAV数据库构造3NF行所需的SQL很复杂:不,它很繁琐但并不复杂。更重要的是,可以提供普通的SQL视图,以便所有用户和报表工具只看到直接的3NF视图,而6NF/EAV问题对他们来说是透明的。最后,所需的SQL可以自动化生成,因此许多人遭受的劳动成本是完全不必要的。

因此,答案实际上是,第六范式作为EAV的先驱和更纯净的形式,是其替代方案。唯一需要注意的是确保正确实现。我有一个大型的6NF数据库,它没有出现人们发布的任何问题,性能良好,客户非常满意(没有进一步工作是完全功能满足的标志)。

我已经在另一个问题中发布了非常详细的答案,该答案也适用于您的问题,您可能会感兴趣。

其他EAV问题


你能否评论一下你实现的具体线索?它似乎并不容易,而且往往会导致完全失败... - Gonzalo Aguilar Delgado

9
无论您使用哪种关系模型,跟踪字段名称更改都需要大量元数据,您必须在事务日志或审计表中跟踪。不幸的是,查询特定日期的状态将非常复杂。然而,如果您的客户仅需要特定时间日期的状态,即整个状态,而不仅仅是关于名称更改的状态,您可以复制数据库并将事务日志回滚到所需的特定时间,并在新实例上运行查询。但是,如果指定日期后添加的实体需要显示旧字段名称的查询,则您面临着一个非常大的工程问题。在这种情况下,根据您在问题中提供的信息,我建议与客户协商替代方案或获取有关报告使用的更多信息以找到替代解决方案。
您可以转移到基于文档的数据存储,但这在第二种情况下仍无法解决问题。很抱歉这并不是一个真正的答案,但是通过类似的情况,客户可能需要更现实的报告解决方案或许多其他投资者愿意为工程提供资金。
当我们遇到这个问题时,我们保持了数据库架构不变,并基于时间戳实施了实体映射工厂。最终,客户不断更改要求(每周到每月一次)关于如何计算聚合字段,并且从未完全满意。

2
非常好的回答。我想补充一点,有些客户可能会完全不满意,因为他们无法接受在数据模型方面终极灵活性和长期一致性之间的权衡。你只需要学会如何管理这样的客户,并防止他们破坏你的生活或声誉。 - Walter Mitty

0

补充一下@NickLarsen和@PerformanceDBA的答案:

如果您需要跟踪字段名称等历史更改,您可能需要查看类似Slowly Changing Dimensions的内容。我觉得您正在使用EAV来建模动态维度模型(可能是查找列表)。

最简单(也可能是效率最低)的实现方法是在EAV表上包含一个“截至日期”字段,每当发生更改时,插入一个新记录(而不是更新现有记录)并使用当前日期。这意味着您需要修改查询以始终包括或查找“截至日期”,或者如果没有提供,则默认为“现在”。然后,连接到EAV对象的基本实体必须查询EAV表中“截至日期”小于或等于行的“最后更新”日期,并按“截至日期”降序排序的“top 1”。最坏的情况是,如果您需要跟踪给定行的名称(存储在“属性”表中)和值都已更改的最近更改,则可以使用行的“最后修改”将此逻辑链接到值表,以查找该特定日期的适当值。
显然,如果有很多更改,这可能会产生大量数据。这就是为什么这种方法被称为“缓慢”变化。它旨在处理可能会发生变化但不经常发生变化的维度值。为了帮助查询性能,应在“截至日期”和“最后修改”字段上建立索引。

0
如果您的客户需要这样的灵活性,那么关系型数据库可能不是最合适的选择。
考虑使用MongoDB存储JSON结构。您可以随意添加或不添加字段,没有限制。您甚至可以使用嵌套功能。

-1
为每个实体描述版本创建一个新的表描述,并创建一个额外的表来告诉你哪个表是哪个版本。查询系统也应该更新。
我认为创建一个生成表和查询的脚本是最好的选择。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接