Oracle:检查特定值的唯一索引
Oracle: unique index with check on a specific value
我有一个简化的 table,其中包含两列(组、键)。
键在组内必须是唯一的,但与 'reference' 组相比也必须是唯一的。
所以我为第一个约束创建了一个唯一索引(组,键),但是是否可以为第二个规则创建一个唯一索引?
基于功能?
一个小例子,如果table包含(组,键):
'reference', 'key1'
'group1', 'key2'
'group2', 'key2'
应拒绝此插入内容:
'group1', 'key1'
谢谢
您可以使用检查约束:
CREATE TABLE <...>(
group <...>,
key <...>,
CONSTRAINT <...> UNIQUE (group, key),
CONSTRAINT <...> CHECK (group != 'group1' OR key != 'key1'))
或者在 table 中插入引用组,并带有表明它不是 "real" 的标志。
我认为您无法将该逻辑放入唯一约束或检查约束中。您可能不得不求助于使用触发器来强制执行此操作:
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(...))
只会return2;计数中的 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
这还将防止为已被任何非引用组使用的密钥添加引用条目。当然,你还需要你独一无二的 constraint/key。
正如@sstan 指出的那样,因为触发器在 DML 完成时触发,而不是在提交时触发,所以两个会话可以同时插入冲突的条目然后都提交,而不会从触发器中看到错误。
一个稍微复杂的方法是创建一个物化视图来计算引用和非引用条目的计数,并对 that 有一个检查约束,它在提交时触发:
-- 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.
并且该会话可以看到(并使用)'bad' 条目:
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
我有一个简化的 table,其中包含两列(组、键)。
键在组内必须是唯一的,但与 'reference' 组相比也必须是唯一的。
所以我为第一个约束创建了一个唯一索引(组,键),但是是否可以为第二个规则创建一个唯一索引? 基于功能?
一个小例子,如果table包含(组,键):
'reference', 'key1'
'group1', 'key2'
'group2', 'key2'
应拒绝此插入内容:
'group1', 'key1'
谢谢
您可以使用检查约束:
CREATE TABLE <...>(
group <...>,
key <...>,
CONSTRAINT <...> UNIQUE (group, key),
CONSTRAINT <...> CHECK (group != 'group1' OR key != 'key1'))
或者在 table 中插入引用组,并带有表明它不是 "real" 的标志。
我认为您无法将该逻辑放入唯一约束或检查约束中。您可能不得不求助于使用触发器来强制执行此操作:
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(...))
只会return2;计数中的 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
这还将防止为已被任何非引用组使用的密钥添加引用条目。当然,你还需要你独一无二的 constraint/key。
正如@sstan 指出的那样,因为触发器在 DML 完成时触发,而不是在提交时触发,所以两个会话可以同时插入冲突的条目然后都提交,而不会从触发器中看到错误。
一个稍微复杂的方法是创建一个物化视图来计算引用和非引用条目的计数,并对 that 有一个检查约束,它在提交时触发:
-- 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.
并且该会话可以看到(并使用)'bad' 条目:
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