【问题标题】:Advice on handling versioning of an EAV-model table关于处理 EAV 模型表版本控制的建议
【发布时间】:2012-10-23 17:09:52
【问题描述】:

Rails 可以使用 many ActiveRecord 版本控制 gem,但大多数(如果不是全部)拥有 trouble being maintained。最重要的是,其中一些似乎有各种foreignkeyassociationissues

我正在编写一个内容管理系统,其中页面存储在树状层次结构中,页面字段使用EAV model 存储在单独的表中。

记住这一点,我并不是在寻找一个包罗万象的修订版宝石,因为老实说,我不认为我会找到一个。我正在寻找的是有关如何将其作为自定义实现来处理的一些建议。我应该有一个单独的表来存储修订并在我的 EAV 表中引用修订号吗?我预见这可能会导致一些复杂的验证问题。无论如何,我目前在寻找一种干净的方法来验证常规 EAV 表时遇到问题,所以如果有人可以对此发表评论,我将不胜感激。

我希望这个问题写得足够好,符合 SO 标准。如果您需要任何其他信息,请随时询问,我会尽力帮助您。 :)

【问题讨论】:

    标签: ruby-on-rails database ruby-on-rails-3 activerecord entity-attribute-value


    【解决方案1】:

    我目前无法找到一种干净的方法来验证常规 无论如何,EAV表,所以如果有人可以对此发表评论,那将是非常 也非常感谢。

    没有一种干净的方式来验证或约束 EAV 表。这就是 DBA 将其称为anti-pattern 的原因。 (EAV 从幻灯片 16 开始。)Bill 没有谈论版本,所以我会。

    版本控制看起来很简单,但事实并非如此。要对行进行版本化,您可以添加一列。它是版本号还是时间戳并不重要。

    create table test (
      test_id integer not null,
      attr_ts timestamp not null default current_timestamp,
      attr_name varchar(35) not null,
      attr_value varchar(35) not null,
      primary key (test_id, attr_ts, attr_name)
    );
    
    insert into test (test_id, attr_name, attr_value) values
    (1, 'emp_id', 1),
    (1, 'emp_name', 'Alomar, Anton');
    
    select * from test;
    
    test_id  attr_ts                      attr_name   attr_value
    --
    1        2012-10-28 21:00:59.688436   emp_id      1
    1        2012-10-28 21:00:59.688436   emp_name    Alomar, Anton
    

    虽然它在输出时可能看起来不像,但所有这些属性值都是 varchar(35)。 dbms 没有简单的方法来阻止某人输入 'wibble' 作为 emp_id。如果您需要类型检查,则必须在应用程序代码中进行。 (而且您必须防止睡眠不足的 DBA 使用 dbms 提供的命令行和 GUI 界面。)

    当然,对于规范化表,您只需将 emp_id 声明为整数类型。

    使用版本控制,更新 Anton 的名字变成了插入。

    insert into test (test_id, attr_name, attr_value) values
    (1, 'emp_name', 'Alomar, Antonio');
    

    使用版本控制,选择有点复杂。您可以使用视图代替公用表表达式。

    with current_values as (
      select test_id, attr_name, max(attr_ts) cur_ver_ts
      from test
      -- You'll probably need an index on this pair of columns to get good performance.
      group by test_id, attr_name
    )
    select t.test_id, t.attr_name, t.attr_value
    from test t
    inner join current_values c 
            on c.test_id = t.test_id
           and c.attr_name = t.attr_name
           and c.cur_ver_ts = t.attr_ts
    
    test_id   attr_name   attr_value
    --
    1         emp_id      1
    1         emp_name    Alomar, Antonio
    

    一个有 100 万行和 8 个不可为空列的规范化表有 100 万行。一个类似的 EAV 表有 800 万行。一个版本化的 EAV 表有 800 万行,加上对每个值和每个属性名称的每次更改都有一行。

    存储版本号并加入包含当前值的第二个表并不会获得太多收益(如果有的话)。每个(传统)插入都需要插入两个表。一行 8 列变成 16 行(两个表各有 8 个)。

    选择稍微简单一些,只需要一个连接。

    【讨论】:

    • 感谢您的回答。很抱歉没有早点检查。我知道这有点反模式。我想我会做我的研究并为我的问题寻找更好的解决方案。很好的答案。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2012-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-21
    • 1970-01-01
    • 1970-01-01
    • 2014-05-10
    相关资源
    最近更新 更多