ORA-04091 TABLE ODB.EMPLOYEE 正在变异,TRIGGER/FUNCTION 可能看不到它。我的触发器有问题吗?
ORA-04091 TABLE ODB.EMPLOYEE IS MUTATING, TRIGGER/FUNCTION MAY NOT SEE IT. IS THERE SOMETHING WRONG WITH MY TRIGGER?
尝试在 Employee Table 的 Status 更新时创建触发器,并为 Employee table 和 Employee_Header table 中的记录捕获一些值记录并发送电子邮件。触发器抛出错误。
CREATE OR REPLACE TRIGGER ODB.TRG_EMPLOYEE_STATUS_EMAIL
AFTER UPDATE OF STATUS ON ODB.EMPLOYEE
FOR EACH ROW
DECLARE
s_message varchar2(4000);
s_subject varchar2(1000);
s_return_message varchar2(4000);
s_database varchar2(50);
v_rm EMPLOYEE%ROWTYPE;
v_sh EMPLOYEE_HEADER%ROWTYPE;
BEGIN
if :old."STATUS" = 'HOLD' AND :new."STATUS" = 'ACTIVE' AND :new."CATEGORY" = 'FULLTIME' then
select * into v_rm from EMPLOYEE WHERE :new."STATUS" = 'ACTIVE' AND ROWNUM>1;
select * into v_sh from EMPLOYEE_HEADER WHERE ROWNUM>1;
s_subject := 'NAME ' || v_rm.NAME ||' message ' || ' CHECK LOG OF EMPLOYEE' || Chr(13) || ' STATUS: ' || v_rm.STATUS ;
s_message := 'SAMPLE' || Chr(10)||Chr(13) || 'THE STATUS IN EMPLOYEE_HEADER IS: ' || Chr(10)|| ' STATUS: ' || v_sh.STATUS ;
pkg_email.sendEmail('INPUT PARAMETERS TO SEND EMAIL');
end if;
END;
您不能 select 来自正在更改的 table;它正在 变异 。但是,因为您可以使用 :new
伪记录,所以您可以“跳过”该错误。此外,where rownum > 1
是无用的,因为它永远不会是真的。不知道你用它想表达什么
我看到您使用双引号创建了列。在 Oracle 中,这通常是 错误 。并不是说它不起作用 - 它会起作用,但您始终必须使用双引号引用列并匹配字母大小写。
最后,触发器可能如下所示(阅读代码中的注释):
create or replace trigger odb.trg_employee_status_email
after update of status on odb.employee
for each row
declare
s_message varchar2(4000);
s_subject varchar2(1000);
s_return_message varchar2(4000);
s_database varchar2(50);
-- v_rm employee%rowtype; -- you don't need that
v_sh employee_header%rowtype;
begin
if :old."status" = 'HOLD' and :new."status" = 'ACTIVE' and
:new."category" = 'FULLTIME'
then
-- You can't select from a table which is just being changed - it is "mutating".
-- Besides, AND ROWNUM > 1 will never return anything. You can only check
-- ROWNUM <= some_value
--select * into v_rm from employee where :new."status" = 'ACTIVE' and rownum>1;
select * into v_sh from employee_header where rownum>1;
-- instead of SELECT ... INTO V_RM, use :NEW pseudorecord
s_subject := 'NAME ' || :new.name ||' message ' || ' CHECK LOG OF EMPLOYEE'
|| chr(13) || ' STATUS: ' || :new.status ;
s_message := 'SAMPLE' || chr(10)||chr(13) || 'THE STATUS IN EMPLOYEE_HEADER IS: ' || chr(10)|| ' STATUS: ' || v_sh.status ;
pkg_email.sendemail('INPUT PARAMETERS TO SEND EMAIL');
end if;
exception
when no_data_found then null;
end;
尝试在 Employee Table 的 Status 更新时创建触发器,并为 Employee table 和 Employee_Header table 中的记录捕获一些值记录并发送电子邮件。触发器抛出错误。
CREATE OR REPLACE TRIGGER ODB.TRG_EMPLOYEE_STATUS_EMAIL
AFTER UPDATE OF STATUS ON ODB.EMPLOYEE
FOR EACH ROW
DECLARE
s_message varchar2(4000);
s_subject varchar2(1000);
s_return_message varchar2(4000);
s_database varchar2(50);
v_rm EMPLOYEE%ROWTYPE;
v_sh EMPLOYEE_HEADER%ROWTYPE;
BEGIN
if :old."STATUS" = 'HOLD' AND :new."STATUS" = 'ACTIVE' AND :new."CATEGORY" = 'FULLTIME' then
select * into v_rm from EMPLOYEE WHERE :new."STATUS" = 'ACTIVE' AND ROWNUM>1;
select * into v_sh from EMPLOYEE_HEADER WHERE ROWNUM>1;
s_subject := 'NAME ' || v_rm.NAME ||' message ' || ' CHECK LOG OF EMPLOYEE' || Chr(13) || ' STATUS: ' || v_rm.STATUS ;
s_message := 'SAMPLE' || Chr(10)||Chr(13) || 'THE STATUS IN EMPLOYEE_HEADER IS: ' || Chr(10)|| ' STATUS: ' || v_sh.STATUS ;
pkg_email.sendEmail('INPUT PARAMETERS TO SEND EMAIL');
end if;
END;
您不能 select 来自正在更改的 table;它正在 变异 。但是,因为您可以使用 :new
伪记录,所以您可以“跳过”该错误。此外,where rownum > 1
是无用的,因为它永远不会是真的。不知道你用它想表达什么
我看到您使用双引号创建了列。在 Oracle 中,这通常是 错误 。并不是说它不起作用 - 它会起作用,但您始终必须使用双引号引用列并匹配字母大小写。
最后,触发器可能如下所示(阅读代码中的注释):
create or replace trigger odb.trg_employee_status_email
after update of status on odb.employee
for each row
declare
s_message varchar2(4000);
s_subject varchar2(1000);
s_return_message varchar2(4000);
s_database varchar2(50);
-- v_rm employee%rowtype; -- you don't need that
v_sh employee_header%rowtype;
begin
if :old."status" = 'HOLD' and :new."status" = 'ACTIVE' and
:new."category" = 'FULLTIME'
then
-- You can't select from a table which is just being changed - it is "mutating".
-- Besides, AND ROWNUM > 1 will never return anything. You can only check
-- ROWNUM <= some_value
--select * into v_rm from employee where :new."status" = 'ACTIVE' and rownum>1;
select * into v_sh from employee_header where rownum>1;
-- instead of SELECT ... INTO V_RM, use :NEW pseudorecord
s_subject := 'NAME ' || :new.name ||' message ' || ' CHECK LOG OF EMPLOYEE'
|| chr(13) || ' STATUS: ' || :new.status ;
s_message := 'SAMPLE' || chr(10)||chr(13) || 'THE STATUS IN EMPLOYEE_HEADER IS: ' || chr(10)|| ' STATUS: ' || v_sh.status ;
pkg_email.sendemail('INPUT PARAMETERS TO SEND EMAIL');
end if;
exception
when no_data_found then null;
end;