【问题标题】:Error Trigger: table is mutating, trigger/function may not see it ORA-06512:错误触发器:表正在变异,触发器/函数可能看不到它 ORA-06512:
【发布时间】:2021-12-20 22:20:57
【问题描述】:

我编写了一个更新后触发器,只要金额列中的一个数字发生变化,它就会重新计算最后一列中所有行的百分比。请帮我摆脱变异错误。

FRUITNAME AMOUNT PERC
Apples 10 null
Pears 20 null
Bananas 6 null
Orange 10 null
Pineapples 4 null
create or replace trigger fruit_perc
after update of AMOUNT
ON FRUIT
FOR EACH ROW
DECLARE
    TOTAL NUMBER;
BEGIN
    FOR i IN (SELECT PERC, AMOUNT, SUM(amount) AS TOTAL FROM FRUIT group by PERC, AMOUNT)
    LOOP
        
        i.PERC := (i.AMOUNT/i.TOTAL) * 100;
        INSERT INTO FRUIT(PERC) VALUES(i.PERC); 
        DBMS_OUTPUT.PUT_LINE(i.amount);
    END LOOP;
END;
/


update FRUIT
set AMOUNT = 4
WHERE AMOUNT = 4;
/

【问题讨论】:

    标签: sql oracle plsql triggers sql-update


    【解决方案1】:

    变异表错误几乎总是指向糟糕的设计,通常是数据模型损坏。当我们有一个触发器在拥有触发器的表上执行 DML 时,就会出现这种情况。这是一个问题,因为触发器在事务期间触发:当触发器触发时,表的状态是不断变化的,因此触发器不可能导出表数据的准确状态。

    考虑您的 UPDATE 语句:如果 SET 子句是 set amount = amount+1,触发器如何为每一行计算 PERC? TOTAL 是更新前的金额总和吗?在更新结束时?在语句处理每一行时累积的某种形式的滚动总数?谁能告诉?当然内核不能,这就是它抛出 ORA-4091 的原因。

    大概这是家庭作业。 PERC 是我们不包含在表中的那种列的经典示例,因为当我们需要它时在 SQL 中派生它比在表上的每个 DML 语句中维护它更有效。

    投影列的正确方法如下:

    with cte as (
      select sum(amount) as tot
      from fruit)
    select fruit.fruitname
          ,fruit.amount
          ,(fruit.amount/cte.tot) as perc
    from fruit
    cross join cte
    /
    

    【讨论】:

    • 把第一段(特别糟糕的设计)放到一个更简单的角度来看:这个错误的存在是为了防止开发者创建一个无限循环。这基本上意味着触发器通常无法对正在触发的表进行操作。
    猜你喜欢
    • 2015-06-28
    • 2016-02-24
    • 2022-10-20
    • 1970-01-01
    • 1970-01-01
    • 2016-04-29
    • 1970-01-01
    • 2010-09-27
    • 2013-11-24
    相关资源
    最近更新 更多