在 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>
我已经尝试了很多方法来创建一个触发器来在该字段在其他寄存器上更新后更新该字段。总是失败。
当我使用 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>