我在名为DM_USER_ROLE
的表上有以下AFTER INSERT触发器create or replace TRIGGER "DM_USER_ROLE_T1"
AFTER
insert on "DM_USER_ROLE"
for each row
DECLARE
v_cert_enrolment_id number;
v_user_role_id number;
begin
v_cert_enrolment_id := "DM_CERTIFICATION_ENROLMEN_SEQ".nextval;
v_user_role_id := :new.USER_ROLE_ID;
/*
When a user is assigned a role, we create an enrolment record
in DM Certification record linked to this user/role combination.
We also insert into the DM_COURSE_ENROLMENT table the courses
associated with the certfication
*/
--FIRST AN ENROLMENT RECORD IS CREATED IN DM_CERTIFICATION_ENROLMENT
INSERT INTO DM_CERTIFICATION_ENROLMENT
(CERTIFICATION_ENROLMENT_ID, ALLOCATED_DT, DEADLINE_DATE, STATUS, USER_ROLE_ID)
VALUES
(
v_cert_enrolment_id,
trunc(sysdate),
trunc(sysdate) + 60,
'Enrolled',
v_user_role_id
);
--COURSES LINKED TO THE CERTIFICATION ARE INSERTED INTO DM_COURSE_ENROLMENT
INSERT INTO DM_COURSE_ENROLMENT
(
CERTIFICATION_ENROLMENT_ID,
COURSE_ID,
ALLOCATED_DT,
DEADLINE_DT,
STATUS
)
SELECT v_cert_enrolment_id,
COURSE.COURSE_ID,
trunc(sysdate),
trunc(sysdate) + 60,
'Enrolled'
FROM DM_CERTIFICATION_COURSE COURSE
WHERE CERTIFICATION_ID =
(
SELECT C.CERTIFICATION_ID FROM
DM_CERTIFICATION_ENROLMENT A,
DM_USER_ROLE B,
DM_ROLE_CERTIFICATION C
WHERE
A.USER_ROLE_ID = B.USER_ROLE_ID
AND
B.ROLE_ID = C.ROLE_ID
AND
A.CERTIFICATION_ENROLMENT_ID = v_cert_enrolment_id
);
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SQLERRM(-20299)));
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SUBSTR(SQLERRM, 1, 2000));
end;
当在此表中发生插入时,我需要填充2个单独的表,我认为AFTER INSERT触发器避免了变异表的问题?
我不确定是什么导致它,也许是DM_USER_ROLE的第二个INSERT语句中的读取,这是启动此触发器的地方......但我的印象是AFTER INSERTs可以安全地避免突变,因为更新已经发生了。
错误是:
ORA-04091:表AZLEARN_BACKUP.DM_USER_ROLE正在变异, 触发器/功能可能看不到它
第一次插入发生,第二次插入不发生。
这篇文章让我相信AFTER触发器是安全的。
http://www.dba-oracle.com/t_avoiding_mutating_table_error.htm
------- UPDATE ---------------
我更改了它,使用两个参数化游标逐行插入并且它有效...仍然不确定错误是什么:
create or replace TRIGGER "DM_USER_ROLE_T1"
AFTER
insert on "DM_USER_ROLE"
for each row
DECLARE
v_cert_enrolment_id number;
v_user_role_id number;
v_role_id number;
v_certification_id number;
cursor certs_for_role(p_role_id number) is
select * from DM_ROLE_CERTIFICATION where ROLE_ID = p_role_id;
r_certs_for_role certs_for_role%rowtype;
cursor courses_for_certs(p_cert_id number) is
select * from DM_CERTIFICATION_COURSE where CERTIFICATION_ID = p_cert_id;
r_courses_for_certs courses_for_certs%rowtype;
begin
v_user_role_id := :new.USER_ROLE_ID;
v_role_id := :new.ROLE_ID;
open certs_for_role(v_role_id);
loop
fetch certs_for_role into r_certs_for_role;
exit when certs_for_role%notfound;
v_cert_enrolment_id := "DM_CERTIFICATION_ENROLMEN_SEQ".nextval;
INSERT INTO DM_CERTIFICATION_ENROLMENT
(CERTIFICATION_ENROLMENT_ID, ALLOCATED_DT, DEADLINE_DATE, STATUS, USER_ROLE_ID, CERTIFICATION_ID)
VALUES
(
v_cert_enrolment_id,
trunc(sysdate),
trunc(sysdate) + 60,
'Enrolled',
v_user_role_id,
r_certs_for_role.CERTIFICATION_ID
);
open courses_for_certs(r_certs_for_role.CERTIFICATION_ID);
loop
fetch courses_for_certs into r_courses_for_certs;
exit when courses_for_certs%notfound;
INSERT INTO DM_COURSE_ENROLMENT
(
CERTIFICATION_ENROLMENT_ID,
COURSE_ID,
ALLOCATED_DT,
DEADLINE_DT,
STATUS
)
VALUES
(
v_cert_enrolment_id,
r_courses_for_certs.COURSE_ID,
trunc(sysdate),
trunc(sysdate) + 60,
'Enrolled'
);
end loop;
close courses_for_certs;
end loop;
close certs_for_role;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SQLERRM(-20299)));
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SUBSTR(SQLERRM, 1, 2000));
end;
答案 0 :(得分:1)
原因很简单,您无法从ROW LEVEL触发器所基于的表DM_USER_ROLE
中进行选择。
在你的第一个解决方案中,你有一个
SELECT ...
FROM DM_USER_ROLE ...
这是不允许的。您的第二个触发器不会选择表DM_USER_ROLE
,因此它正在运行。
链接页面中的建议是正确的但是当他们在“或”而不是“触发器”之后声明“使用”时会产生误导 - 应该更准确地说“使用”后面的语句“或”而不是“触发器”。 Oracle根据以下操作提供触发器:
你有一个可以有不同时间点的DML触发器:
很多人都错过了语句级触发器和行级触发器之间的区别。
行级触发器具有关键字FOR EACH ROW
,并像关键字暗示的那样为每一行运行。如果跳过FOR EACH ROW
关键字,则无论您的INSERT / UPDATE / DELETE语句有多少行影响,每个语句只执行一次触发器。
答案 1 :(得分:0)
变异表错误的最可能原因是滥用triggers
。这是一个典型的例子:
1.您在表A中插入一行
2.表A上的触发器(对于每一行)在表A上执行查询,例如计算汇总列
3.Oracle抛出一个ORA-04091:表A正在变异,触发器/函数可能看不到它
这是一种预期的正常行为,Oracle希望保护您自己,因为Oracle保证:
•(i)每个陈述都是原子的(即要么失败要么成功 完全地)
•(ii)每个陈述都能看到一致的数据视图
现在在您的触发器中,当您执行第二次插入时,它正在DM_USER_ROLE
表上进行连接以获取记录,这就是您面临的原因
ORA-04091:表AZLEARN_BACKUP.DM_USER_ROLE正在变异, 触发器/功能可能看不到它