【问题标题】:Compound Triggers复合触发器
【发布时间】:2020-06-17 13:32:06
【问题描述】:

问题 - 想要通过使用复合触发器来避免突变触发器的问题。但是做不到

背景 - 每当主表“CUSTOM_ITEM”发生变化时,我想在新表“跟踪表”中插入数据

设计是这样的,每次在表中创建一行时,都会生成一个 ITEM_ID,但在某些情况下会有一个列 FIRST_ITEM_ID 保持不变。

因此,每当添加新行时,我都想检查其 FIRST_ITEM_ID,然后检查整个表并找出具有相同 FIRST_ITEM_ID 的所有 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 ;
/

【问题讨论】:

    标签: plsql triggers oracle12c mutating-table


    【解决方案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。您需要的只是使用必要的 ID 构建您的收藏。然后在 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;       
    

    【讨论】:

    • 感谢@Belayer 的回复。这种方法奏效了。我面临的唯一问题是它在跟踪表中插入记录“两次”
    • 可能只是再次插入。我最初以为,由于您的要求没有明确说明不这样做,所以我没有这样做。我已经更新了答案。我已经更新了第二个版本,同样可以应用于第一个。
    • 非常感谢@Belayer。您是否有任何指向解释为什么它插入两次的文档的链接。为什么循环的触发器性能很差
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-03
    • 2011-01-01
    • 2016-05-21
    • 1970-01-01
    • 1970-01-01
    • 2020-12-30
    • 2016-08-20
    相关资源
    最近更新 更多