Составные триггеры

Проблема. Хотите избежать проблемы изменения триггеров с помощью составного триггера. Но не в силах это сделать

Предыстория. Я хочу вставлять данные в новую таблицу «Таблица отслеживания» всякий раз, когда в основной таблице «CUSTOM_ITEM» происходят изменения.

Дизайн таков, что каждый раз, когда в таблице создается строка, генерируется ITEM_ID, но есть столбец FIRST_ITEM_ID, который в некоторых случаях остается неизменным.

Поэтому всякий раз, когда добавляется новая строка, я хочу проверить ее FIRST_ITEM_ID, а затем проверить всю таблицу и узнать все ITEM_ID, имеющие тот же самый FIRST_ITEM_ID.

И я хочу вставить все эти строки в новую таблицу с помощью триггера.

Это вообще возможно?

Присоединение триггера:

CREATE OR REPLACE TRIGGER APP.TEST_TRG
FOR DELETE OR INSERT OR UPDATE 
ON APP.CUSTOM_ITEM
COMPOUND TRIGGER

TYPE t_change_tab IS TABLE OF APP.TEST_TRG.OBJECT_ID%TYPE;
g_change_tab t_change_tab := t_change_tab();

BEFORE EACH ROW IS
  BEGIN


      Select item_id bulk collect into g_change_tab from CUSTOM_ITEM where first_item_id =
     (Select first_item_id from CUSTOM_ITEM where item_id = :NEW.item_id);


        For i in 1 .. g_change_tab.COUNT()
            LOOP 

            g_change_tab.extend;

            END LOOP;    

  END BEFORE EACH ROW;

AFTER STATEMENT IS

    BEGIN
    For i in 1 .. g_change_tab.COUNT()
    LOOP

        app.bc_acs_pkg.populate_TEST_TRG     /* Package Inserts data */
                (p_object_type => 'ITEM',
                p_object_id => g_change_tab(i));


    END LOOP;

    g_change_tab.delete;
  END AFTER STATEMENT;


END ;
/

person chitra tolani    schedule 05.03.2020    source источник


Ответы (1)


Вы можете делать то, что хотите, просто не с вашим нынешним подходом. Давайте сделаем шаг назад. Что такое исключение изменяющейся таблицы (ORA-04091). Он вызывается при попытке доступа к таблице, для которой сработал триггер в событии уровня строки, что не разрешено. Просто создание составного триггера не снимает это ограничение. Итак, в вашем сегменте «Перед рядом» утверждение

Select item_id 
  bulk collect into g_change_tab 
  from CUSTOM_ITEM where first_item_id =
     (Select first_item_id from CUSTOM_ITEM where item_id = :NEW.item_id);

является недействительным и приводит к возникновению ORA-04091. Что вам нужно, так это просто создать свою коллекцию с необходимыми идентификаторами. Затем обработайте их в сегменте оператора After.

create or replace trigger test_trg
for delete or insert or update 
on custom_item
compound trigger
  type t_change_tab is 
       table of custom_item.first_item%type; 

  g_change_tab t_change_tab := t_change_tab();

before each row is
      l_first_item_exists boolean := false;
      indx                integer; 
  begin
      indx := g_change_tab.first;  
      while not l_first_item_exists
        and indx is not null 
      loop
          l_first_item_exists := (g_change_tab(indx) = :new.first_item);
          if not l_first_item_exists
          then 
              indx := g_change_tab.next(indx);
          end if;               
      end loop; 

      if not l_first_item_exists
      then
        g_change_tab.extend;
        g_change_tab(g_change_tab.last) := :new.first_item;
      end if; 
end before each row;

after statement is
  begin
     for indx in g_change_tab.first .. g_change_tab.last
     loop 
         insert into tracking_table(item_id, first_item)
         select item_id, first_item
           from custom_item 
          where first_item = g_change_tab(indx);
     end loop;
end after statement;
end test_trg;

Проблема здесь в циклах, всегда самой медленной обработке и очень плохих триггерах. Ниже приведен подход, который полностью их избегает. Однако это требует создания массива типов на уровне схемы.

create or replace type custom_item_change_t is 
                   table of integer ; --custom_item.first_item%type;

create or replace trigger test_trg
for insert
on custom_item
compound trigger     
  g_change_tab custom_item_change_t := custom_item_change_t();

before each row is
  begin
    g_change_tab.extend;
    g_change_tab(g_change_tab.last) := :new.first_item; 
end before each row;

after statement is
  begin
   insert into tracking_table(item_id, first_item)
     select item_id, first_item
       from custom_item ci
      where first_item in (select distinct column_value
                             from table(g_change_tab)
                          ) 
        and not exists 
            ( select null 
                from tracking_table tt
               where ci.item_id = tt.item_id
                 and ci.first_item = tt.first_item
            ); 
end after statement;
end test_trg;       
person Belayer    schedule 05.03.2020
comment
Спасибо @Belayer за ответ. Этот подход сработал. Единственная проблема, с которой я сталкиваюсь, заключается в том, что она дважды вставляет записи в таблицу отслеживания. - person chitra tolani; 09.03.2020
comment
Это, вероятно, но вставка снова. Сначала я подумал, что, поскольку в вашем требовании конкретно не указано, что этого делать не нужно, я этого не делал. Я обновил ответ, чтобы сделать это. Я обновил 2-ю версию, то же самое можно применить и к 1-й. - person Belayer; 09.03.2020
comment
Большое спасибо @Belayer. У вас есть ссылка на документацию, объясняющую, почему она вставляется дважды. И почему производительность триггеров плохая с циклами - person chitra tolani; 10.03.2020