триггер для обновления в 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 мутирует, триггер/функция может его не видеть

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: НОВЫЕ или СТАРЫЕ ссылки не допускаются в триггерах уровня таблицы


person albertoivo    schedule 26.05.2021    source источник


Ответы (1)


Просто

:new.cdefault := 0;  

(колонку нельзя назвать default, она зарезервирована)


Как вы заметили, что это не сработает, так как вы обновите всю таблицу, ну - это ерунда. Взглянуть.

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>

После того, как вы (наконец-то) приняли решение и сказали нам, что для каждого человека может быть несколько адресов, я бы предложил составной триггер, который исправляет ошибку изменяющейся таблицы.

С моей точки зрения, у вас должно быть две таблицы — person (главная) и address (детальная; содержит все адреса для каждого человека; имеет внешний ключ, указывающий на таблицу person).

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>
person Littlefoot    schedule 26.05.2021
comment
Я не могу этого сделать. Я поменяю весь стол. Я хочу изменить только адрес по умолчанию для определенного идентификатора. (Каждый ID может иметь много адресов) - person albertoivo; 26.05.2021
comment
Мне очень жаль. Это моя ошибка. Я не объяснил, в чем проблема. У каждого человека может быть более одного адреса, но только один по умолчанию. Поэтому, когда человек помечает один конкретный адрес как адрес по умолчанию, остальные должны быть нулевыми. - person albertoivo; 26.05.2021
comment
Хорошо, тогда; это составной триггер, который устраняет проблему изменяющейся таблицы. Я отредактировал ответ и добавил пример. Посмотрите, пожалуйста. - person Littlefoot; 26.05.2021
comment
Большое спасибо! - person albertoivo; 26.05.2021