如何使用返回多个值的子查询编写正确的触发器
How to write a correct trigger with a subquery which is returning more than one value
我需要创建一个触发器,而不是在视图中插入一行,而是在 3 个表中插入值。
这是正确的触发器:
INSTEAD OF INSERT ON Retete_vegetariane
FOR EACH ROW
BEGIN
INSERT INTO categorie(categ_id, tip)
VALUES(:NEW.categ_id, :NEW.tip);
INSERT INTO Ingredient(ingred_id, ingredient)
VALUES(:NEW.ingred_id, :NEW.ingredient);
INSERT INTO Reteta(reteta_id, nume, descriere, categ_id, vegetariana, timp_preparare, portii)
VALUES(:NEW.reteta_id, :NEW.reteta, :NEW.descriere, :NEW.categ_id,'D', :NEW.timp_preparare, :NEW.portii);
INSERT INTO Set_Ingrediente(reteta_id, ingred_id, cantitate, um, comentarii)
VALUES (:NEW.reteta_id, :NEW.ingred_id, :NEW.cantitate, :NEW.um, :NEW.comentarii);
END;
/
现在我想更新触发器以在 reteta_id 已经存在时引发应用程序错误。它仍然引发错误说 reteta_id 是主键,但现在我想让它说它已经存在。
这是我试过的代码,但它说你不能在触发器中使用这样的子查询。
CREATE OR REPLACE TRIGGER ad_vegetarian
INSTEAD OF INSERT ON Retete_vegetariane
FOR EACH ROW
DECLARE
variabil reteta.reteta_id%type;
BEGIN
variabil:=:NEW.reteta_id;
IF variabil EXISTS(
SELECT DISTINCT reteta_id FROM reteta;) THEN
RAISE_APPLICATION_ERROR(-20512,'Reteta_id already exists');
END IF;
INSERT INTO categorie(categ_id, tip)
VALUES(:NEW.categ_id, :NEW.tip);
INSERT INTO Ingredient(ingred_id, ingredient)
VALUES(:NEW.ingred_id, :NEW.ingredient);
INSERT INTO Reteta(reteta_id, nume, descriere, categ_id, vegetariana, timp_preparare, portii)
VALUES(:NEW.reteta_id, :NEW.reteta, :NEW.descriere, :NEW.categ_id,'D', :NEW.timp_preparare, :NEW.portii);
INSERT INTO Set_Ingrediente(reteta_id, ingred_id, cantitate, um, comentarii)
VALUES (:NEW.reteta_id, :NEW.ingred_id, :NEW.cantitate, :NEW.um, :NEW.comentarii);
END;/
将子查询移出 IF
:
...
begin
select nvl(max(1), 0)
into variabil
from dual
where exists (select null
from reteta
where reteta_id = :new.reteta_id
);
if variabil = 1 then
raise_application_error(-20512, 'Reteta_id already exists');
end if;
...
如果您查看 IF
, you'll find that it accepts boolean_expression
after IF
. boolean_expression
定义的文档,则在 conditional_predicate
分支中列出这些替代方案:
{ collection.EXISTS ( index )
| expression { IS [ NOT ] NULL
| [ NOT ] { BETWEEN expression AND expression
| IN ( expression [, expression ]... )
| LIKE pattern
}
| relational_operator expression
}
| { named_cursor | SQL } % { FOUND | ISOPEN | NOTFOUND }
}
它不允许你放置 EXISTS
谓词,因为它是一个 SQL 谓词。
您可以避免双重检查(在 select 和后续插入时)是否存在:执行 insert
并处理 dup_val_on_index
异常。
create table t1 (
id1 int primary key,
val1 varchar2(100)
)
create table t2 (
id2 int primary key,
val2 varchar2(100),
id1 references t1(id1)
)
create view v_test as
select *
from t1
join t2
using(id1)
create trigger trg_test
instead of insert on v_test
for each row
begin
insert into t1(id1, val1)
values (:new.id1, :new.val1);
begin
insert into t2(id2, val2, id1)
values(:new.id2, :new.val2, :new.id1);
exception
when dup_val_on_index then
RAISE_APPLICATION_ERROR(-20512,'Reteta_id already exists');
end;
end;
/
insert into v_test(id1, val1, id2, val2)
values(1, 'A', 1, 'B')
1 rows affected
insert into v_test(id1, val1, id2, val2)
values(2, 'AA', 1, 'B')
ORA-20512: Reteta_id already exists
ORA-06512: at "FIDDLE_CPKPBNEZVLNFZLUOGPNZ.TRG_TEST", line 10
ORA-04088: error during execution of trigger 'FIDDLE_CPKPBNEZVLNFZLUOGPNZ.TRG_TEST'
db<>fiddle here
我需要创建一个触发器,而不是在视图中插入一行,而是在 3 个表中插入值。 这是正确的触发器:
INSTEAD OF INSERT ON Retete_vegetariane
FOR EACH ROW
BEGIN
INSERT INTO categorie(categ_id, tip)
VALUES(:NEW.categ_id, :NEW.tip);
INSERT INTO Ingredient(ingred_id, ingredient)
VALUES(:NEW.ingred_id, :NEW.ingredient);
INSERT INTO Reteta(reteta_id, nume, descriere, categ_id, vegetariana, timp_preparare, portii)
VALUES(:NEW.reteta_id, :NEW.reteta, :NEW.descriere, :NEW.categ_id,'D', :NEW.timp_preparare, :NEW.portii);
INSERT INTO Set_Ingrediente(reteta_id, ingred_id, cantitate, um, comentarii)
VALUES (:NEW.reteta_id, :NEW.ingred_id, :NEW.cantitate, :NEW.um, :NEW.comentarii);
END;
/
现在我想更新触发器以在 reteta_id 已经存在时引发应用程序错误。它仍然引发错误说 reteta_id 是主键,但现在我想让它说它已经存在。 这是我试过的代码,但它说你不能在触发器中使用这样的子查询。
CREATE OR REPLACE TRIGGER ad_vegetarian
INSTEAD OF INSERT ON Retete_vegetariane
FOR EACH ROW
DECLARE
variabil reteta.reteta_id%type;
BEGIN
variabil:=:NEW.reteta_id;
IF variabil EXISTS(
SELECT DISTINCT reteta_id FROM reteta;) THEN
RAISE_APPLICATION_ERROR(-20512,'Reteta_id already exists');
END IF;
INSERT INTO categorie(categ_id, tip)
VALUES(:NEW.categ_id, :NEW.tip);
INSERT INTO Ingredient(ingred_id, ingredient)
VALUES(:NEW.ingred_id, :NEW.ingredient);
INSERT INTO Reteta(reteta_id, nume, descriere, categ_id, vegetariana, timp_preparare, portii)
VALUES(:NEW.reteta_id, :NEW.reteta, :NEW.descriere, :NEW.categ_id,'D', :NEW.timp_preparare, :NEW.portii);
INSERT INTO Set_Ingrediente(reteta_id, ingred_id, cantitate, um, comentarii)
VALUES (:NEW.reteta_id, :NEW.ingred_id, :NEW.cantitate, :NEW.um, :NEW.comentarii);
END;/
将子查询移出 IF
:
...
begin
select nvl(max(1), 0)
into variabil
from dual
where exists (select null
from reteta
where reteta_id = :new.reteta_id
);
if variabil = 1 then
raise_application_error(-20512, 'Reteta_id already exists');
end if;
...
如果您查看 IF
, you'll find that it accepts boolean_expression
after IF
. boolean_expression
定义的文档,则在 conditional_predicate
分支中列出这些替代方案:
{ collection.EXISTS ( index )
| expression { IS [ NOT ] NULL
| [ NOT ] { BETWEEN expression AND expression
| IN ( expression [, expression ]... )
| LIKE pattern
}
| relational_operator expression
}
| { named_cursor | SQL } % { FOUND | ISOPEN | NOTFOUND }
}
它不允许你放置 EXISTS
谓词,因为它是一个 SQL 谓词。
您可以避免双重检查(在 select 和后续插入时)是否存在:执行 insert
并处理 dup_val_on_index
异常。
create table t1 ( id1 int primary key, val1 varchar2(100) )
create table t2 ( id2 int primary key, val2 varchar2(100), id1 references t1(id1) )
create view v_test as select * from t1 join t2 using(id1)
create trigger trg_test instead of insert on v_test for each row begin insert into t1(id1, val1) values (:new.id1, :new.val1); begin insert into t2(id2, val2, id1) values(:new.id2, :new.val2, :new.id1); exception when dup_val_on_index then RAISE_APPLICATION_ERROR(-20512,'Reteta_id already exists'); end; end; /
insert into v_test(id1, val1, id2, val2) values(1, 'A', 1, 'B')
1 rows affected
insert into v_test(id1, val1, id2, val2) values(2, 'AA', 1, 'B')
ORA-20512: Reteta_id already exists ORA-06512: at "FIDDLE_CPKPBNEZVLNFZLUOGPNZ.TRG_TEST", line 10 ORA-04088: error during execution of trigger 'FIDDLE_CPKPBNEZVLNFZLUOGPNZ.TRG_TEST'
db<>fiddle here