在 Oracle 中触发更新

trigger to update in Oracle

我已经尝试了很多方法来创建一个触发器来在该字段在其他寄存器上更新后更新该字段。总是失败。

当我使用 FOR EACH ROW 时,我得到 mutating table 错误。而且当我尝试直接更新时,我不知道如何引用新值。

每个人可以有多个地址,但默认只有一个。因此,当此人将一个特定地址标记为默认地址时,其他地址必须为零。

CREATE OR REPLACE TRIGGER trg_aiur_default_address
BEFORE UPDATE ON address FOR EACH ROW
BEGIN
    UPDATE address SET default_address = 0
    WHERE person_id = :NEW.id;
END;
/

ORA-04091: table person is mutating, trigger/function may not see it

CREATE OR REPLACE TRIGGER trg_aiur_default_address
BEFORE UPDATE ON address
BEGIN
    UPDATE address SET default_address = 0
    WHERE person_id = :NEW.id;
END;
/

ORA-04082: NEW or OLD references not allowed in table level triggers

就是

:new.cdefault := 0;  

(不能命名列default,它是保留的)


正如您评论说它不会工作,因为您将“更新整个 table”,好吧 - 那是胡说八道。看看。

SQL> create table person as
  2    select 1 id, 200 cdefault from dual union all
  3    select 2 id, 350 cdefault from dual;

Table created.

SQL> create or replace trigger trg_aiur_default_address
  2    before update on person
  3    for each row
  4  begin
  5      :new.cdefault := 0;
  6  end;
  7  /

Trigger created.

SQL> update person set id = 500 where id = 1;

1 row updated.

SQL> select * from person;

        ID   CDEFAULT
---------- ----------
       500          0            --> see? CDEFAULT = 0
         2        350            --> for ID = 2 nothing changed

SQL>

在您(最终)下定决心并告诉我们每个人可以有多个地址之后,我建议使用 复合触发器 来“修复”变异 table 错误。

从我的角度来看,你应该有两个 tables - person (master) 和 address (detail; 包含每个人的所有地址; 它有一个外国指向 person table).

的键
SQL> create table person
  2    (id       number primary key,
  3     name     varchar2(20)
  4    );

Table created.

SQL> create table address
  2    (id         number primary key,
  3     id_pers    number references person (id),
  4     address    varchar2(30),
  5     cb_default number(1) default 0 not null
  6    );

Table created.

SQL> insert into person (id, name)
  2    select 1, 'Littlefoot' from dual union all
  3    select 2, 'Alberto'    from dual;

2 rows created.

SQL> insert into address (id, id_pers, address, cb_default)
  2    select 1, 1, 'London', 1 from dual union all
  3    select 2, 1, 'Paris' , 0 from dual union all
  4    select 3, 1, 'Berlin', 0 from dual union all
  5    select 4, 2, 'Zagreb', 1 from dual;

4 rows created.

SQL>

复合触发器:

SQL> create or replace trigger trg_bu_dflt_addr
  2    for update on address
  3    compound trigger
  4
  5    type t_rec is table of address%rowtype;
  6    l_tab t_rec;
  7
  8    before statement is
  9      begin
 10        l_tab := t_rec();
 11      end before statement;
 12
 13    before each row is
 14    begin
 15      null;
 16    end before each row;
 17
 18    after each row is
 19      begin
 20        l_tab.extend;
 21        l_tab(l_tab.count()).id := :new.id;
 22        l_tab(l_tab.count()).id_pers := :new.id_pers;
 23        l_tab(l_tab.count()).cb_default := :new.cb_default;
 24      end after each row;
 25
 26    after statement is
 27      begin
 28        for i in 1 .. l_tab.count() loop
 29          if l_tab(i).cb_default = 1 then
 30             update address a set
 31               a.cb_default = 0
 32               where a.id_pers = l_tab(i).id_pers
 33                 and a.id <> l_tab(i).id;
 34          end if;
 35        end loop;
 36      end after statement;
 37  end;
 38  /

Trigger created.

测试:我当前的默认地址在伦敦;我会改成柏林。

SQL> select * From address order by id;

        ID    ID_PERS ADDRESS                        CB_DEFAULT
---------- ---------- ------------------------------ ----------
         1          1 London                                  1  --> my current default address
         2          1 Paris                                   0
         3          1 Berlin                                  0
         4          2 Zagreb                                  1

SQL> -- setting my default address to Berlin
SQL> update address set cb_default = 1
  2    where id_pers = 1
  3      and id = 3;

1 row updated.

SQL> select * from address order by id;

        ID    ID_PERS ADDRESS                        CB_DEFAULT
---------- ---------- ------------------------------ ----------
         1          1 London                                  0
         2          1 Paris                                   0
         3          1 Berlin                                  1  --> my new default address
         4          2 Zagreb                                  1

SQL>