【问题标题】:I cant find solution for the trigger mutating in PL/SQL我找不到 PL/SQL 中触发器变异的解决方案
【发布时间】:2020-12-30 23:05:07
【问题描述】:

所以我是 PL/SQL 的新手,我想创建一个触发器,其中不能更新或删除特定记录的薪水,而表的其他记录可以。假设我不想更新或删除其薪水的记录是EMPNO = 7839,触发器被创建,但是当我更新 EMP 表中的任何记录时,它给了我ORA-04091: table SCOTT.EMP is mutating, trigger/function may not see it 的错误,有人可以给我一个解决方案吗? 这是代码:

CREATE OR REPLACE TRIGGER PRACTICE_TRIGGER
BEFORE DELETE OR UPDATE OF SAL ON EMP
FOR EACH ROW
DECLARE
ROW_NUM NUMBER;
BEGIN
SELECT COUNT(*) INTO ROW_NUM FROM EMP WHERE EMPNO = 7839;
IF UPDATING('ROW_NUM') THEN
RAISE_APPLICATION_ERROR('-20000','CANT UPDATE/DELETE SALARY OF EMPNO = 7839');
END IF;
END PRACTICE_TRIGGER;
/

【问题讨论】:

  • 除了技术问题,还有什么商业案例?为什么我们要阻止一位特定员工的薪水变动?如果规则扩展到或更改到其他员工怎么办?你不想总是重新编译触发器来处理这样的变化。
  • 另外,你是说不只删除工资值,还是不删除整条记录?
  • @pmdba 我说的是不删除工资值。
  • @BHS 然后你的触发器不需要触发“删除或更新”,只需更新。删除是针对被删除的整个记录​​。更新用于更改列的值。

标签: oracle plsql triggers


【解决方案1】:

首先,您的查询选择的是 of 条记录,而不是记录标识符 - 它永远不会返回“7839”,只返回“1”或“0”来表示找到的记录数。此外,您不能从触发器中引用触发器所属的表(这是您的变异表错误)。最后,'ROW_NUM' 不是表中的列,它是触发器中的变量,因此“IF UPDATING('ROW_NUM') 始终为假,假设它完全可以编译。

你正在寻找的最基本的形式是这样的:

CREATE OR REPLACE TRIGGER PRACTICE_TRIGGER
BEFORE DELETE OR UPDATE OF SAL ON EMP
FOR EACH ROW
BEGIN
  -- check to see if record being updated is restricted, then raise error
  IF :OLD.EMPNO = 7839 THEN
    RAISE_APPLICATION_ERROR('-20000','CANT UPDATE/DELETE SALARY OF EMPNO = 7839');
  END IF;
END PRACTICE_TRIGGER;
/

也就是说,这种方法的一个明显缺陷是,所写的触发器不会阻止某人更改员工 ID,因此理论上如果有人先更改了它,那么对工资更改的限制将不起作用。一种更有效的方法是使用布尔列(真/假)来识别锁定的记录并检查是否设置了该标志。我还建议使用表 API 包来执行实际的 DML 操作,而不是直接使用 SQL 命令,并尽可能避免使用触发器。

【讨论】:

    【解决方案2】:

    您可以将您的代码转换成这个:

    CREATE OR REPLACE TRIGGER PRACTICE_TRIGGER
    BEFORE DELETE OR UPDATE OF SAL ON EMP
    FOR EACH ROW
    BEGIN    
     IF :OLD.EMPNO = 7839 AND :OLD.SAL != NVL(:NEW.SAL,0) THEN
       RAISE_APPLICATION_ERROR('-20000','CAN''T UPDATE SALARY OR DELETE THE ROW FOR EMPNO = '||:OLD.EMPNO);
     END IF;
    END;
    /
    

    在哪里

    • 不需要查询。只是有关的新旧版本 员工的SAL 值应该相等,以保持该值(7839) 保持在表内。对于DELETING 情况,列的:NEW 值将为NULL
    • 这些条件对DELETINGUPDATING 都有效,所以不 需要在代码中重复它们。但是一列不能删除,会删除整条记录
    • 在末尾重复触发器名称是可选的,因此可能是 已删除。

    Demo

    【讨论】:

    • 你能解释一下:OLD.EMPNO != NVL(:NEW.EMPNO,0)这行的作用吗?
    • 您不能在删除触发器中使用 :NEW.EMPNO ,而 UPDATE OF SAL 正是原始问题中所述的业务规则(“我不希望能够更新或删除其薪水”) ,所以它需要留下来。
    • @pmdba 您不能在删除触发器中使用 :NEW.EMPNO ,请查看 this而 UPDATE OF SAL 正是业务规则 ,谢谢,我错过了。
    • @BHS 我将其替换为:OLD.SAL != NVL(:NEW.SAL,0),以便比较我之前错过的相关薪水列的过去和当前值。对于删除案例:NEW.SAL 的值将是NULL,所以NVL 代表这个。
    • @BarbarosÖzhan 感谢您提供的信息,您能否告诉我如何使用名称而不是使用 EMPNO = 7839 来代替 ENAME = 'KING' 之类的名称?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-12
    • 1970-01-01
    相关资源
    最近更新 更多