正确设计EAV数据库以获取历史数据

Xeo*_*oss 1 sql foreign-keys key-value entity-attribute-value 6nf

介绍

我一直在阅读有关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]
Run Code Online (Sandbox Code Playgroud)

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
        }
Run Code Online (Sandbox Code Playgroud)

像这样的表将使我们永远不必这样做,UPDATE ...因为我们只需要INSERT INTO ...为每个更改值的新属性添加,就created_at可以知道最新的值。这非常适合保存历史数据记录(当然也可以例外)。

样本查询

首先,实体是什么“类型”?(用户,帖子,评论等。)

SELECT * FROM entity_type et LEFT JOIN entity e ON e.entity_type_id = et.id WHERE e.id = ?
Run Code Online (Sandbox Code Playgroud)

接下来,该实体的属性是什么?(表属性)

SELECT * FROM attr WHERE entity_id = ?
Run Code Online (Sandbox Code Playgroud)

接下来,此实体的属性中存在哪些值?(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
...
Run Code Online (Sandbox Code Playgroud)

该实体存在什么关系?

假设我们有一个ID为34的“发布”实体,并且我们想要它的“注释”(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;
Run Code Online (Sandbox Code Playgroud)

除了多个查询(无论如何,键值存储都需要这些查询)之外,这种方法还会存在哪些问题?

phi*_*pxy 6

一个EAV“数据库” [原文如此]是字面上 数学 直截了当一个未记录的描述在数据库和其元数据的三元组,没有功能tablulate关系,或查询的关系,或查询的元数据,或类型检查或维护完整性,或优化,或原子交易,或控制并发。

软件工程原理规定,合理的EAV数据库的使用完全由定义适当的抽象(类型,运算符,过程,解释器,模块)组成,以重建DBMS的功能。

从一个人的EAV三元组及其含义到(碎片化的)数据库描述的映射的机械性质使这一点易于显示。

Greenspun来解释,任何足够复杂的EAV项目都包含一个临时的,非正式指定的,臭虫缠身的,缓慢执行的DBMS一半。

我再说一遍:EAV是数据库及其元数据的三元组的无文档描述,没有DBMS。仅对已证明DDL解决方案不能满足性能要求并且EAV解决方案可以而且值得的数据库部分使用EAV。


归档时间:

查看次数:

918 次

最近记录:

8 年,6 月 前