我想学习一种声明式方法,用于处理与独占日期范围有关的数据约束问题。
以下是一个简化示例。我有这些项目的物品和价格。我希望价格的有效日期范围是相互排斥的,没有重叠。
正如我对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' );
答案 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
这种声明性方法既是并发的,也是一致的。但是有很多缺点:
FAST REFRESH
工作可能是一场噩梦,不仅仅是最简单的查询。即使是这个简单的例子,我也不得不使用旧式连接,并且不得不添加无用的ROWID。COMMIT
之前不会强制执行约束。虽然这可能是一个积极的事情,因为许多类型的变化会暂时产生重叠的结果。如果从不允许重叠结果,则必须按特定顺序修改表。