如何在时态表中强制执行键唯一性?

时间:2014-06-25 07:33:12

标签: sql oracle constraints

在时态表(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);

有两个限制要强制执行表的时间性质,即:

  1. 对于每条记录,我们必须有END_PERIOD> START_PERIOD。这是Key-> Value地图有效的时间段。

  2. 对于每个密钥,不能有任何重叠期。该期间包括START_PERIOD的时刻,但不包括END_PERIOD的确切时刻。

  3. 可以在行插入/更新或提交时执行约束。只要不可能提交无效数据,我就不在乎。

  4. 我已经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中的无效行?

3 个答案:

答案 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的空值指示键的常量值。