用于约束表中数据范围的声明方法

时间:2013-07-16 16:57:40

标签: sql oracle oracle11g date-range

我想学习一种声明式方法,用于处理与独占日期范围有关的数据约束问题。

以下是一个简化示例。我有这些项目的物品和价格。我希望价格的有效日期范围是相互排斥的,没有重叠。

正如我对Oracle的理解user-defined functions are not eligible for use in CONSTRAINT declarations - 我甚至无法想象如果它被允许会有多糟糕。所以我需要一个使用触发器的程序方法。典型的触发源也包含在下面。

我对学习更好的程序逻辑以便在触发器中使用感兴趣(这只是一个简单的演示示例)。我有兴趣学习一个更具声明性的解决方案来解决我(也可能是其他人)面临的相对流行的数据约束问题。

作为一种做法,我想尽可能地消除基于触发器的解决方案。但是,如果没有触发器,我似乎无法找到解决这个问题的方法。

create table item ( title varchar2(32) primary key );
create table price ( 
   item           varchar2(32) not null references item (title), 
   price          number(9,2), 
   effective_from date not null, 
   effective_to   date not null, 
   constraint price_from_to_ck check (effective_to > effective_from ));

[REDACTED]
*(A combination of row and statement level triggers inteneded to prevent logical chronological overlap)

insert into item values ('LETTUCE');
insert into item values ('WHISKY');

insert into price values ( 'LETTUCE', 1.05, date '2013-01-01', date '2013-03-31' );
insert into price values ( 'LETTUCE', 1.08, date '2013-04-01', date '2013-06-30' ); 
insert into price values ( 'WHISKY', 33.99, date '2013-01-01', date '2013-05-31' );
insert into price values ( 'WHISKY', 31.15, date '2013-06-01', date '2013-07-31' ); 

-- should fail
insert into price values ( 'WHISKY', 30.55, date '2013-05-15', date '2013-06-05' ); 

2 个答案:

答案 0 :(得分:2)

在等待支持Temporal Validity的下一个Oracle 12c版本时,我仍然使用下一个方法:

create table item ( title varchar2(32) primary key );
create table price ( 
   price_id          number primary key,
   item              varchar2(32) not null references item (title), 
   price             number(9,2), 
   effective_from    date not null, 
   effective_to      date not null, 
   effective_prev_to date,
   constraint price_from_to_ck check ( effective_to > effective_from ),
   constraint price_to_prev_ck check ( effective_from = effective_prev_to + 1 ),
   constraint price_from_uq unique ( item, effective_to ),
   constraint price_dates_chain_fk foreign key ( item, effective_prev_to ) references price ( item, effective_to ) );

insert into item values ('LETTUCE');
insert into item values ('WHISKY');

insert into price values ( 1, 'LETTUCE', 1.05, date '2013-01-01', date '2013-03-31', null );
insert into price values ( 2, 'LETTUCE', 1.08, date '2013-04-01', date '2013-06-30', date '2013-03-31' ); 
insert into price values ( 3, 'WHISKY', 33.99, date '2013-01-01', date '2013-05-31', null );
insert into price values ( 4, 'WHISKY', 31.15, date '2013-06-01', date '2013-07-31', date '2013-05-31' ); 

我们try

insert into price values ( 5, 'WHISKY', 30.55, date '2013-05-15', date '2013-06-05', date '2013-05-14' ); 

ORA-02291: integrity constraint (USER_4_E7DF1.PRICE_DATES_CHAIN_FK) violated - parent key not found : insert into price values ( 'WHISKY', 30.55, date '2013-05-15', date '2013-06-05', date '2013-05-14' )

但现在更新和删除链中间的日期是痛苦的屁股。它需要在MERGE的一个语句中更改前后行。这就是我添加price_id列的原因,因为您无法更新MERGE中的密钥 - 因此,您需要另一个密钥而不是(item,effective _%)。

答案 1 :(得分:0)

你可以使用物化视图来明确地做到这一点,正如Brian Camire首先提出的那样。这是一个例子:

--Original tables (with an extra primary key on PRICE)
create table item ( title varchar2(32) primary key );
create table price ( 
   id             number primary key,
   item           varchar2(32) not null references item (title), 
   price          number(9,2), 
   effective_from date not null, 
   effective_to   date not null, 
   constraint price_from_to_ck check (effective_to > effective_from ));

create materialized view log on price with rowid;

--Items with overlapping dates
create materialized view price_no_overlap_mv
refresh fast on commit as
select 'overlapping row' as dummy, price1.rowid rowid1, price2.rowid rowid2
from price price1, price price2
where
    --Same item
    price1.item = price2.item
    --Overlapping dates
    and (price1.effective_from <= price2.effective_to and price1.effective_to >= price2.effective_from) 
    --Don't compare the same row
    and price1.id <> price2.id
;

--Throw an error if any rows ever get created.
alter table price_no_overlap_mv
add constraint price_no_overlap_mv_ck check (dummy = 'no rows allowed');


insert into item values ('LETTUCE');
insert into item values ('WHISKY');

insert into price values (1, 'LETTUCE', 1.05, date '2013-01-01', date '2013-03-31' );
insert into price values (2, 'LETTUCE', 1.08, date '2013-04-01', date '2013-06-30' ); 
insert into price values (3, 'WHISKY', 33.99, date '2013-01-01', date '2013-05-31' );
insert into price values (4, 'WHISKY', 31.15, date '2013-06-01', date '2013-07-31' ); 
commit;

-- should fail
insert into price values (5, 'WHISKY', 30.55, date '2013-05-15', date '2013-06-05' ); 
commit;
ORA-12008: error in materialized view refresh path
ORA-02290: check constraint (JHELLER.PRICE_NO_OVERLAP_MV_CK) violated

这种声明性方法既是并发的,也是一致的。但是有很多缺点:

  1. 仅在Enterprise Edition中支持快速刷新所需的物化视图日志。
  2. 您的表需要一个主键,尽管您可能已经有一个主键,但只是在示例中没有包含它。
  3. 虽然声明性,但解决方案仍然不是直截了当的。您必须声明相反的条件,然后检查它是否永远不存在。
  4. FAST REFRESH工作可能是一场噩梦,不仅仅是最简单的查询。即使是这个简单的例子,我也不得不使用旧式连接,并且不得不添加无用的ROWID。
  5. COMMIT之前不会强制执行约束。虽然这可能是一个积极的事情,因为许多类型的变化会暂时产生重叠的结果。如果从不允许重叠结果,则必须按特定顺序修改表。