在时态表(Oracle DBMS)中强制执行密钥唯一性的最佳方法是什么。时态表是以时间跨度记录所有历史状态的表。
例如,我们有一个Key - >像这样的价值联想...
create table TEMPORAL_VALUES
(KEY1 varchar2(99) not null,
VALUE1 varchar2(99),
START_PERIOD date not null,
END_PERIOD date not null);
有两个限制要强制执行表的时间性质,即:
对于每条记录,我们必须有END_PERIOD> START_PERIOD。这是Key-> Value地图有效的时间段。
对于每个密钥,不能有任何重叠期。该期间包括START_PERIOD的时刻,但不包括END_PERIOD的确切时刻。
可以在行插入/更新或提交时执行约束。只要不可能提交无效数据,我就不在乎。
我已经informed that the best practice强制执行这样的约束,即使用物化视图而不是触发器。
请告知实现这一目标的最佳方法是什么?
Oracle横幅是......
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
我认为这个解决方案很接近,但它并没有真正发挥作用,因为在提交时#'需要。 Oracle似乎无法创建这种复杂性的物化视图,该视图会在提交时刷新。
create materialized view OVERLAPPING_VALUES
nologging cache build immediate
refresh complete on demand
as select 'Wrong!'
from
(
select KEY1, END_PERIOD,
lead( START_PERIOD, 1) over (partition by KEY1 order by START_PERIOD) as NEXT_START
from TEMPORAL_VALUES
)
where NEXT_START < END_PERIOD;
alter table OVERLAPPING_VALUES add CHECK( 0 = 1 );
我做错了什么?如何通过提交来防止TEMPORAL_VALUES中的无效行?
答案 0 :(得分:1)
我已经看到了一种针对SQL Server描述的技术(请参阅this article并搜索&#34; Kuznetsov的历史表&#34;),它增加了第三个时间列,{ {1}}可用于在表本身上建立外键以强制执行间隔不能重叠的约束。我不知道这是否可以适应Oracle。
答案 1 :(得分:1)
经过this forum post的一些挣扎,实验和指导,
drop table TEMPORAL_VALUE;
create table TEMPORAL_VALUE
(KEY1 varchar2(99) not null,
VALUE1 varchar2(99),
START_PERIOD date not null,
END_PERIOD date
)
/
alter table TEMPORAL_VALUE add
constraint CHECK_PERIOD check ( END_PERIOD is null or END_PERIOD > START_PERIOD)
/
alter table TEMPORAL_VALUE add
constraint PK_TEMPORAL_VALUE primary key (KEY1, START_PERIOD)
/
alter table TEMPORAL_VALUE add
constraint UNIQUE_END_PERIOD unique (KEY1, END_PERIOD)
/
create materialized view log on TEMPORAL_VALUE with rowid;
drop materialized view OVERLAPPING_VALUES;
create materialized view OVERLAPPING_VALUES
build immediate refresh fast on commit as
select a.rowid a_rowid, b.rowid b_rowid
from TEMPORAL_VALUE a, TEMPORAL_VALUE b
where a.KEY1 = b.KEY1
and a.rowid <> b.rowid
and a.START_PERIOD <= b.START_PERIOD
and (a.END_PERIOD is null or (a.END_PERIOD > b.START_PERIOD));
alter table OVERLAPPING_VALUES add CHECK( 0 = 1 );
为什么这样做,但我原来发布的视图......
select KEY1, END_PERIOD,
lead( START_PERIOD, 1) over (partition by KEY1 order by START_PERIOD) as NEXT_START
from TEMPORAL_VALUES
...不会被接受为On-Commit物化视图?那么,答案是,提交实体化视图的复杂性似乎有限。视图必须包含基础表的行标识或键,而不是超过某个复杂度的阈值。
答案 2 :(得分:1)
很好的解决方案肖恩!
但是由于复杂性,我会为你的对象添加注释......如:
COMMENT ON COLUMN TEMPORAL_VALUE.KEY IS 'Each key may have at most only one value for any instant in time';
COMMENT ON COLUMN TEMPORAL_VALUE.START_PERIOD IS 'The period described includes the START_PERIOD date/time';
COMMENT ON COLUMN TEMPORAL_VALUE.END_PERIOD IS 'The period described does not included the END_PERIOD date/time. A null end period means until forever';
COMMENT ON COLUMN TEMPORAL_VALUE IS 'Integrity is enforced by the MATERIALIZED VIEW OVERLAPPING_VALUES';
COMMENT ON MATERIALIZED VIEW OVERLAPPING_VALUES IS 'Used to enforce the rule - each key may have at most only one value for any instant in time. This is an [on commit] mv, that holds any temporal values that overlaps another (for the same key), but the CHECK(0=1) constraint will raise an exception if any rows are found, stopping any commit that would break integrity';
我个人希望使用V_
为MV_和视图添加所有物化视图名称的前缀有趣的是,您不允许START_PERIOD为空。大多数实现允许空启动和非空结束指定之前的所有内容,并且两个bates的空值指示键的常量值。