ORA-04091:表CISTU018.TIMESHEET正在变异,触发器/函数可能看不到它

时间:2017-03-28 16:20:16

标签: oracle

我创建了一个触发器:(我之前的触发器运行良好,但是我的教练要我做一个触发后,但是当我试图触发触发器时它没有用)

/*AFTER TRIGGER*/
SET SERVEROUTPUT ON;
SET VERIFY OFF
CREATE OR REPLACE TRIGGER HOUR_TIMESHEET
AFTER INSERT ON TIMESHEET
FOR EACH ROW

DECLARE
T_REGHOURS TIMESHEET.REGHOURS%TYPE := :NEW.REGHOURS;
T_OTIMEHOURS TIMESHEET.OTIMEHOURS%TYPE := :NEW.REGHOURS - 40;
T_EMPID TIMESHEET.EMPID%TYPE := :NEW.EMPID;
T_PAYWEEKENDDATE TIMESHEET.PAYWEEKENDDATE%TYPE := :NEW.PAYWEEKENDDATE;
BEGIN
  IF(T_REGHOURS > 40)
  THEN
  DBMS_OUTPUT.PUT_LINE('Employee ID:'|| T_EMPID);
  UPDATE TIMESHEET
    SET REGHOURS = 40, OTIMEHOURS = (T_OTIMEHOURS) WHERE EMPID = :OLD.EMPID AND PAYWEEKENDDATE = :OLD.PAYWEEKENDDATE;
  END IF;
END;

/*end of trigger*/

脚本输出:编译的触发器:Trigger HOUR_TIMESHEET编译

然后我尝试插入以触发触发器: 插入时间表值(72690,'30 -MAY-03',42,0);

然后它给了我一条错误信息:

Error starting at line : 142 in command -
INSERT INTO TIMESHEET VALUES(72690,'30-MAY-03',42,0)
Error report -
SQL Error: ORA-04091: table CISTU018.TIMESHEET is mutating, trigger/function may not see it
ORA-06512: at "CISTU018.HOUR_TIMESHEET", line 10
ORA-04088: error during execution of trigger 'CISTU018.HOUR_TIMESHEET'
04091. 00000 -  "table %s.%s is mutating, trigger/function may not see it"
*Cause:    A trigger (or a user defined plsql function that is referenced in
           this statement) attempted to look at (or modify) a table that was
           in the middle of being modified by the statement which fired it.
*Action:   Rewrite the trigger (or function) so it does not read that table.
Employee ID:72690

1 个答案:

答案 0 :(得分:0)

行级TRIGGER正文中的更新语句是禁止的,因为它从附加触发器的TABLE读取。在oracle行级触发器中禁止对附加表进行任何SELECT或操作 如果您的目标是将新TIMESHEET条记录中的剩余(> 40)正常小时数转换为加班时间,则根本不需要在语句中进行更新,并且可以使用{{1} } BEFORE直接改变传入的数据,但正如您提到的教师要求TRIGGER,我将包含一个AFTER TRIGGER示例。我不确定这是建议还是要求,所以我还会对AFTER触发器进行一些讨论,因为这些可能更好,具体取决于具体情况。

这是一个使用BEFORE触发器的简单示例,假设没有涉及其他触发器。在此示例中,AFTER时间更改为语句而不是TRIGGER。这消除了您在原始错误中观察到的限制,并将剩余时间从正常工时中拉出来,并将其添加到加班时间。

首先,创建表格:

ROW

然后创建语句级CREATE TABLE TIMESHEET( EMPID NUMBER(10,0) NOT NULL, REGHOURS NUMBER(3,0) NOT NULL, OTIMEHOURS NUMBER(3,0) NOT NULL, PAYWEEKENDDATE DATE NOT NULL); AFTER

TRIGGER

然后通过插入一些数据进行测试:

CREATE OR REPLACE TRIGGER HOUR_TIMESHEET
AFTER INSERT ON TIMESHEET
BEGIN
UPDATE TIMESHEET
SET REGHOURS = 40,
OTIMEHOURS = (OTIMEHOURS + REGHOURS - 40)
WHERE REGHOURS > 40;
END;
/

然后检查TRIGGER的作用:

--reghours <= 40 and no overtime present. No change expected
INSERT INTO TIMESHEET VALUES (1,40, 0, TO_DATE('20170401','YYYYMMDD'));
-- reghours < 40 and overtime already counted.  No change expected
INSERT INTO TIMESHEET VALUES (2,20, 15, TO_DATE('20170401','YYYYMMDD'));
-- reghours (50) > 40, plus 10 additional overtime hours.  Expecting adjustment to 40 + 20
INSERT INTO TIMESHEET VALUES (1,50, 10, TO_DATE('20170415','YYYYMMDD'));
-- reghours (55) > 40, plus no overtime.  Expecting adjustment to 40 + 15
INSERT INTO TIMESHEET VALUES (2,55, 0, TO_DATE('20170415','YYYYMMDD'));
COMMIT;

触发器已SELECT * FROM TIMESHEET ORDER BY 4 ASC, 1 ASC; EMPID REGHOURS OTIMEHOURS PAYWEEKENDDATE 1 40 0 01-APR-17 2 20 15 01-APR-17 1 40 20 15-APR-17 2 40 15 15-APR-17 AFTER已完成,并已更新剩余INSERTREGHOURS,但仅剩下其他OTIMEHOURSREGHOURS 。希望这符合你的目标。

使用行级和语句级触发器有利有弊,决定使用什么可能取决于所涉及的数据模型以及其他触发器等。
另一种方法是使用OTIMEHOURS触发器直接改变传入的BEFORE字段的值,正如您在问题中提到的那样。请注意,这可能是更低成本和更可取的,具体取决于所涉及的数据以及其他:NEW是否正在进行中。权衡声明与行的利弊可能是有益的。

建议尽可能简化触发器。但是,如果你真的需要采取行动来利用对附表的选择并要求行级评估,或者需要调整或细化语句级触发器,我建议调查Compound-Triggers < / p>