我应该使用触发器还是检查约束,我是否可以创建执行以下工作的检查约束:
如果某篇文章的类型为“Bert”,那么它只能在mytable中有一行,而在库存中只有1行。 如果它不是'Bert'类型,那么这两个条件都不适用。(可以在库存中有更多行和多于1行)
INSERT INTO mytable VALUES('Chip',1,'Bert'); -- Yes
INSERT INTO mytable VALUES('Screen',1,'Bert'); -- Yes
INSERT INTO mytable VALUES('Chip',1,'Bert'); -- No
INSERT INTO mytable VALUES('Cable',2,'Bert'); -- No
INSERT INTO mytable VALUES('Keyboard',2,'Non-bert'); -- Yes
INSERT INTO mytable VALUES('Keyboard',2,'Non-bert'); -- Yes
INSERT INTO mytable VALUES('Keyboard',3,'Non-bert'); -- Yes
这样的触发器导致问题,因为我试图访问触发器内的基表:
CREATE OR REPLACE TRIGGER trg_mytable
BEFORE INSERT OR UPDATE ON mytable
FOR EACH ROW
WHEN (NEW.TYPEB = 'Bert')
DECLARE
L_COUNT NUMBER;
BEGIN
SELECT COUNT(*) INTO L_COUNT
FROM MYTABLE
WHERE ARTICLE = :NEW.ARTICLE
AND TYPEB = :NEW.TYPEB;
IF L_COUNT > 0 THEN
RAISE_APPLICATION_ERROR( -20001, 'Bert already exists!' );
ELSIF :NEW.STOCK_COUNT > 1 THEN
RAISE_APPLICATION_ERROR( -20001, 'Can''t insert more than one with type Bert!' );
END IF;
END;
答案 0 :(得分:3)
为确保表中只有一个Bert,您可以在此虚拟列上使用虚拟列和唯一约束的组合。这利用了UNIQUE约束允许多个NULL值的事实:
create table mytable(category varchar2(30), stock number, name varchar2(30));
-- virtual column for checking uniqueness
alter table mytable add is_bert generated always as (
case name when 'Bert' then 1 else null end
);
alter table mytable add constraint uq_bert unique(is_bert);
为确保Bert始终具有等于1的股票,您可以使用带有简单布尔检查的CHECK约束(这假设名称为非NULL):
-- check constraint for ensuring stock for bert equals 1
alter table mytable add constraint chk_bert_has_stock_one check(
name != 'Bert' or stock = 1);
关于触发器与约束的选择,有一个简单的规则:
从不使用触发器来强制执行业务逻辑。它们很难调试,容易出错,并且在多用户环境中无法正常工作,请参阅
<强>更新强>
上述解决方案不符合更新的要求,因为(正如@LalitKumarB正确指出的那样)它错误地拒绝Bert的多行。对于这个更复杂的场景,我将使用带有REFRESH ON COMMIT
的物化视图(这假设mytable有一个主键)和两个以不同方式处理Bert和Non-Bert行的约束:
create materialized view log on mytable;
create materialized view mv_mytable
refresh on commit as
select
(case when type = 'Bert' then name
else null
end) as name,
(case when type = 'Bert' and stock_count != 1 then null
else stock_count
end) as stock_count
from mytable;
alter table mv_mytable add constraint uq_bert unique(name);
alter table mv_mytable add constraint chk_bert_stock_count
check(stock_count is not null);
这种解决方案的缺点是它不会立即拒绝假行;在尝试COMMIT(类似于延迟约束)之前,你不会注意到任何错误。
答案 1 :(得分:-1)
如果typeb
(=&#39; Bert&#39;?)应该是唯一的,只需创建一个唯一索引:create unique index myIndex on mytable ( typeb)
,您在尝试插入时会收到重复的输入错误
答案 2 :(得分:-1)
我们可以使用这样的设置。请修改Frank Schmitt的is_bert专栏,以检查Bert购买的独特类别。
create table mytable(category varchar2(30) , stock number, name varchar2(30));
alter table mytable add is_bert generated always as (
case when name='Bert' and stock=1 then category
else null end
);
alter table mytable add constraint uq_bert unique(is_bert);
alter table mytable add constraint chk_bert_has_stock_one check(
name != 'Bert' or stock = 1);
INSERT INTO mytable(category,stock,name) VALUES('Chip',1,'Bert'); -- Yes
INSERT INTO mytable(category,stock,name) VALUES('Screen',1,'Bert'); -- Yes
INSERT INTO mytable(category,stock,name) VALUES('Chip',1,'Bert'); -- No
INSERT INTO mytable(category,stock,name) VALUES('Cable',2,'Bert'); -- No
INSERT INTO mytable(category,stock,name) VALUES('Keyboard',2,'Non-bert'); -- Yes
INSERT INTO mytable(category,stock,name) VALUES('Keyboard',2,'Non-bert'); -- Yes
INSERT INTO mytable(category,stock,name) VALUES('Keyboard',3,'Non-bert'); -- Yes
我猜也是基于函数的索引可以实现它。
create table mytable(category varchar2(30) , stock number, name varchar2(30));
alter table mytable add constraint chk_bert_has_stock_one check(
name != 'Bert' or stock = 1);
create unique index myfun_index on mytable (case when name='Bert' and stock=1 then category
else null end);