在触发器中使用更新

using update in trigger

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

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'

有什么想法吗?

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

这是我制作的触发器:

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;

这是开始使用触发器时非常常见的问题。 除了通过 :NEW:OLD pseudo-records。这个想法是table处于不断变化的状态,所以它不能被触发器查询或修改..

处理此问题的典型方法是创建 两个 触发器:一个 row-level 触发器后跟一个 statement-level 触发器。 row-level 触发器注册包中的所有更改(包可以保存状态),而 statement-level 触发器跟进并根据行中发生的情况应用所有必要的修改--a statement-level触发器可以修改基础table.

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

在那篇文章中,Steven Feuerstein 不仅描述了问题的传统 package-based 解决方案,而且还提供了更现代的 compound-trigger-based 解决方案。

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

--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

干杯!!