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

时间:2015-09-06 16:14:41

标签: plsql

我有以下错误:

  

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

在此触发器上PL / SQL:

create or replace trigger T_Ordine
after insert on Ordine
for each row

DECLARE
conta number := 0;
t_o exception;

BEGIN
select count(*) into conta
from Cliente join Ordine on Cliente.ID_Cliente = Ordine.ID_CLiente
where Cliente.C_CF_Rivenditore <> Ordine.CF_Rivenditore;

if conta > 0 then
raise t_o;
end if;

EXCEPTION
when t_o then
raise_application_error(-20002,'Un rivenditore non puo ricevere ordini da un cliente non suo');
end;
/

我认为在表Ordine的联接中修改表Cliente导致错误。

2 个答案:

答案 0 :(得分:2)

你的触发器有点奇怪。

您已将其声明为for each row,但您从未使用:new来访问任何已插入的值。

据我所知,有两种方法可以修复你的触发器:

  1. 使触发器成为语句级触发器,以便在插入ordine表后运行一次,无论插入多少行。为此,只需删除行for each row

  2. 调整触发器以仅检查插入的顺序,而不是检查表中的每个顺序。为此,请使用以下内容替换用于查找conta的SQL查询:

    select count(*) into conta
    from Cliente 
    where Cliente.ID_Cliente = :new.ID_CLiente
    and Cliente.C_CF_Rivenditore <> :new.CF_Rivenditore;
    

    请注意,我们不再查询Ordine表 - 刚刚插入的行的详细信息可用:new.column_name。这可以解决ORA-04091错误。

  3. 我会推荐第二种方法。您用于查找conta的查询当前搜索整个Ordine表,并且随着您的应用程序获得越来越多的订单,当查询搜索越来越多的数据时,此触发器会变得越来越慢。此外,您可能不希望您的应用程序拒绝接受任何人的任何订单,如果它发生在系统中某个位置,客户端Rivenditore没有&#39} ; \ t匹配订单&#39; s Rivenditore

    顺便说一下,提出异常t_o,抓住它并引发替代异常并不是很重要。立即提出第二个例外,即:

    if conta > 0 then
    raise_application_error(-20002,'Un rivenditore non puo ricevere ordini da un cliente non suo');
    end if;
    

答案 1 :(得分:1)

由于我是意大利人,我在理解你要做的事情方面有点优势:

  1. “Ordine”是订单表(如产品订单)。
  2. “rivenditore”的意思是“卖方”。
  3. “cliente”指客户。
  4. 在“客户”表中有一个字段(C_CF_Rivenditore),它强制卖方应该用于客户发出的订单。
  5. “订单”表包含对客户的引用以及对收到订单的卖方的引用。
  6. 您只是想让卖家不能为每个客户指定的订单插入订单(这是您的错误消息所说的),但您不知道如何使用:new或:old,所以你用这种方式编写了测试(这并不是最好的方法,因为每次插入新订单时你都会重新检查表中的所有订单。)

    这是你真正想写的:

          create or replace trigger T_Ordine
           after insert on Ordine
           for each row
    
          DECLARE
            rivenditore_del_cliente  Cliente.C_CF_Rivenditore%type;
    
          BEGIN
           select  Cliente.C_CF_Rivenditore
             into  rivenditore_del_cliente
             from Cliente
            where Cliente.ID_Cliente = :new.ID_CLiente
    
            if rivenditore_del_cliente <> :new.CF_Rivenditore  then
                raise raise_application_error(-20002,
                             'Un rivenditore non puo ricevere ordini da un cliente non suo');
            end if;  
          end;
    

    如果其中一些是真的,可能需要进一步检查上述触发器:

    1. id_cliente不是“cliente”的主键
    2. ordine.id_cliente不是强制性的
    3. 没有外键约束来确保ordine.id_cliente是clienti表的有效ID_cliente。