【问题标题】:using update in trigger在触发器中使用更新
【发布时间】:2019-06-17 16:50:43
【问题描述】:

我有删除触发器 我想删除警察,但是当我删除他时,他是他们老板的所有其他警察将在他们的老板字段中为空 所以我使用了这段代码

create or replace trigger switch_boss
before delete
on policeman 
for each row
declare
boss number; 
begin
boss := :new.bossid;
if(:new.policemanid = :new.bossid)then
select policemanid into boss from 
(select * from policeman
order by dbms_random.value)
where rownum =1;

end if;

update policeman
set bossid = boss
where bossid = :new.policemanid;

end switch_boss;

我有错误

ORA-04091: table SYSTEM.POLICEMAN is mutating, trigger/function may not see it
ORA-06512: at "SYSTEM.SWITCH_BOSS", line 13
ORA-04088: error during execution of trigger 'SYSTEM.SWITCH_BOSS'

有什么想法吗?

更新: 我使用复合触发它的工作,但不是我想要的。 我想将被删除的警察的老板设置为他的老板。 问题是删除时我不能现在哪个警察将已删除的警察作为老板。 我可以找到它们,因为它们在删除后的字段中为空,但它们可能属于其他已删除的警察。

这是我做的触发器:

create or replace trigger switch_boss
  for delete
  on policeman 
compound trigger

  after statement is
  cursor c is select * from policeman where bossid is null for update;
  boss number;

  begin
    for r in c loop

      select policemanid into boss from 
                         (select * from policeman order by dbms_random.value)
                         where rownum =1;

      update policeman
      set bossid = boss
      where current of c;
   end loop;
  end after statement;

end switch_boss;

【问题讨论】:

标签: sql oracle plsql database-trigger


【解决方案1】:

在开始使用触发器时,这是一个非常常见的问题。 除了通过:NEW:OLD 伪记录之外,Oracle 不会让您的行级 触发器访问其基表(POLICEMAN)。思路是表处于不断变化的状态,所以不能被触发器查询或修改..

处理这种情况的典型方法是创建两个触发器:一个行级触发器,然后是一个语句级触发器。 行级触发器注册包中的所有更改(包可以保持状态),而语句级触发器跟进并根据行中发生的情况应用所有必要的修改——语句级触发器 可以修改基表。

以下是有关如何执行此操作的说明:Get rid of mutating table trigger errors with the compound trigger

在那篇文章中,Steven Feuerstein 不仅描述了该问题的传统基于包的解决方案,而且他还提供了一种更现代的基于复合触发器的解决方案。

【讨论】:

    【解决方案2】:

    是的,要处理这种情况,您需要以下触发器组合:

    --First of all, You will need one package to hold the values:
    CREATE OR REPLACE PACKAGE MY_VALUE_HOLDER AS
        POLICEMANID NUMBER;
        BOSS NUMBER;
    END MY_VALUE_HOLDER;
    /
    
    -- Before each row trigger
    CREATE OR REPLACE TRIGGER SWITCH_BOSS_ROW_TRG BEFORE
        DELETE ON POLICEMAN
        FOR EACH ROW
    DECLARE
        BOSS   NUMBER;
    BEGIN
        BOSS := :OLD.BOSSID;
        IF ( :OLD.POLICEMANID = :OLD.BOSSID ) THEN
            SELECT
                POLICEMANID
            INTO BOSS
            FROM
                (
                    SELECT
                        *
                    FROM
                        POLICEMAN
                    ORDER BY
                        DBMS_RANDOM.VALUE
                )
            WHERE
                ROWNUM = 1;
    
        END IF;
    
        MY_VALUE_HOLDER.POLICEMANID := :OLD.POLICEMANID;
        MY_VALUE_HOLDER.BOSS := BOSS;
    END SWITCH_BOSS_ROW_TRG;
    /
    
    
    --After statement trigger
    CREATE OR REPLACE TRIGGER SWITCH_BOSS_ST_TRG AFTER
        DELETE ON POLICEMAN
    BEGIN
        UPDATE POLICEMAN
        SET
            BOSSID = MY_VALUE_HOLDER.BOSS
        WHERE
            BOSSID = MY_VALUE_HOLDER.POLICEMANID;
    
    END SWITCH_BOSS_ST_TRG;
    /
    

    第一个触发器在每一行之前,第二个触发器在语句之后。

    DB Fiddle demo

    干杯!!

    【讨论】:

    • 你不能在第一次触发时在表上进行选择。因为它是触发器所针对的表。在后面的语句中,该字段更改为 null,因此您无法找到旧警察的行
    • 你看过演示吗? - @מיכאל ביתן
    • 我看到了,但它不认为可以工作,我试过了,但我们无法运行触发器所针对的表的查询,突变错误。它也与boss字段有onari关系,所以当你删除它时它会变为null
    • 不。相信实践而不是理论。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-07
    • 1970-01-01
    • 2016-11-03
    • 2013-09-09
    相关资源
    最近更新 更多