Oracle:检查特定值的唯一索引

时间:2015-06-18 09:55:38

标签: sql oracle constraints

我有一个包含两列(group,key)的简化表。

密钥在组内必须是唯一的,但与“参考”组相比,它也必须是唯一的。

所以我为第一个约束创建了一个唯一索引(group,key),但是是否可以为第二个规则创建一个唯一索引? 基于功能?

一个小例子,如果表包含(group,key):

'reference', 'key1'
'group1',    'key2'
'group2',    'key2'

这个插入物应该被拒绝:

'group1',    'key1'

由于

2 个答案:

答案 0 :(得分:1)

我认为您不能将该逻辑纳入唯一或检查约束。您可能不得不求助于使用触发器强制执行此操作:

create trigger trg_ref_grp_check
after insert or update on t42
declare
  l_cnt pls_integer;
begin
  select max(count(distinct case when group_id = 'reference' then 1 else 0 end))
  into l_cnt
  from t42
  group by key;

  if l_cnt > 1 then
    raise_application_error(-20001, 'Key defined for reference and group');
  end if;
end;
/

如果相同的密钥用于参考和一个或多个组,则max(count(...))将仅返回2;计数中的case语句将每个条目减少为'reference'或'non-reference',并且计算每个条目的不同值将为每个键提供1或2 - 如果它变为2,则您同时具有引用和非引用参考组值。如果发生这种情况,触发器会抛出异常:

insert into t42 (group_id, key) values ('reference', 'key1');
insert into t42 (group_id, key) values ('group1', 'key2');
insert into t42 (group_id, key) values ('group2', 'key2');

insert into t42 (group_id, key) values ('group1', 'key1');

SQL Error: ORA-20001: Key defined for reference and group
ORA-06512: at "SCHEMA.TRG_REF_GRP_CHECK", line 10
ORA-04088: error during execution of trigger 'SCHEMA.TRG_REF_GRP_CHECK'

select * from t42;

GROUP_ID   KEY 
---------- -----
reference  key1 
group1     key2 
group2     key2 

这也将阻止为任何非参考组已使用的密钥添加引用条目。当然,你仍然需要你的唯一约束/键。

正如@sstan指出的那样,因为触发器在DML完成时触发而不是在它被提交时触发,两个会话可以同时插入冲突的条目然后都提交,而不会看到触发器的错误。

稍微复杂一点的方法是创建一个物化视图来计算引用和非引用条目的计数,并对上的 进行检查约束:

-- drop trigger trg_ref_grp_check

create materialized view log on t42 with rowid;

create materialized view mv_ref_grp
refresh on commit
as
select key,
  count(case when group_id = 'reference' then 1 end) as ref_cnt,
  count(case when group_id != 'reference' then 1 end) as nonref_cnt
from t42
group by key;

alter table mv_ref_grp add constraint chk_ref_grp_cnt
  check (ref_cnt = 0 or nonref_cnt = 0);

您不能在案例中使用distinct,因此视图正在计算引用和非引用外观的总计数,然后约束在提交时检查其中一个是零。它确实意味着您稍后会看到错误:

insert into t42 (group_id, key) values ('group1', 'key1');

1 row inserted.

该会话可以看到(并使用)“坏”条目:

select * from t42;

GROUP_ID   KEY 
---------- -----
reference  key1 
group1     key2 
group2     key2 
group1     key1 

但是当你提交异常时,会抛出错误的条目:

commit;

SQL Error: ORA-12008: error in materialized view refresh path
ORA-02290: check constraint (SCHEMA.CHK_REF_GRP_CNT) violated
12008. 00000 -  "error in materialized view refresh path"

select * from t42;

GROUP_ID   KEY 
---------- -----
reference  key1 
group1     key2 
group2     key2 

答案 1 :(得分:0)

您可以使用检查约束:

CREATE TABLE <...>(
group <...>,
key   <...>,
CONSTRAINT <...> UNIQUE (group, key),
CONSTRAINT <...> CHECK  (group != 'group1' OR key != 'key1'))

或者在表格中插入参考组,其中的标记表示它不是&#34;真实&#34;。