无论如何,我目前在寻找一种干净的方法来验证常规 EAV 表时遇到问题,因此,如果有人可以对此发表评论,我将不胜感激。
没有一种干净的方法来验证或约束 EAV 表。这就是 DBA 将其称为反模式的原因。(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 个)。
选择稍微简单一些,只需要一个连接。