在触发器中使用更新

时间:2019-06-17 16:50:43

标签: sql oracle plsql database-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;

2 个答案:

答案 0 :(得分:0)

使用触发器开始时,这是一个非常常见的问题。 除了通过POLICEMAN:NEW伪记录外,Oracle不允许您的行级触发访问其基表(:OLD)。这个想法是因为该表处于不断变化的状态,因此触发器无法查询或修改它。.

处理此问题的一种典型方法是创建两个 触发器:行级触发器,后跟语句级触发器。 行级触发器将所有更改记录在包中(包可以保留状态),而语句级触发器将跟进并根据行中发生的事情应用所有必要的修改-语句级触发器< em>可以修改基表。

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

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

答案 1 :(得分:0)

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

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

干杯!