【问题标题】:PL/SQL Trigger - Dynamically reference :NEW or :OLDPL/SQL 触发器 - 动态引用 :NEW 或 :OLD
【发布时间】:2010-10-15 19:56:46
【问题描述】:

是否可以动态引用 :NEW/OLD 伪记录,或者复制它们?

我正在为一个非常宽的表执行审核触发器,因此希望避免为插入/删除/更新设置单独的触发器。

更新/插入时我想在审计表中记录 :NEW 值,删除时我想记录 :OLD 值。

【问题讨论】:

    标签: oracle plsql triggers


    【解决方案1】:

    为什么不使用 Oracle 内置的标准或细粒度审计?

    【讨论】:

      【解决方案2】:

      您可以使用复合触发器并以编程方式检查它是否为 I/U/D。

      Compound Triggers

      【讨论】:

      • 这就是我正在做的,但我试图避免在触发器中有多个插入语句。
      • 我不确定我是否关注,你在做这样的事情吗?在 TBL 上插入或更新或删除之前创建或替换触发器 TRG 开始 IF INSERTING THEN ...(record :new) ELSEIF UPDATING THEN ...(record :new) ELSEIF DELETING THEN ...(record :old) END IF; ?
      • 我有一个插入语句 INSERT INTO HIST (EMP_ID, NAME) VALUES (:NEW.EMP_ID , :NEW.NAME) ;但是,在删除时,我想使用 :OLD ,而不是没有单独的插入语句。
      • 答案是否定的。你不能说 VALUES ( DECODE(DELETING, TRUE, :OLD, :NEW).EMP_ID
      【解决方案3】:

      你可以试试:

      declare
        l_deleting_ind varchar2(1) := case when DELETING then 'Y' end;
      begin
        insert into audit_table (col1, col2)
        values
         ( CASE WHEN l_deleting_ind = 'Y' THEN :OLD.col1 ELSE :NEW.col1 END
         , CASE WHEN l_deleting_ind = 'Y' THEN :OLD.col2 ELSE :NEW.col2 END
         );
      end;
      

      我发现该变量是必需的 - 您不能在插入语句中直接访问 DELETING。

      【讨论】:

        【解决方案4】:

        哇,您只想在触发器中插入一个来避免什么?

        “我有一个插入语句 INSERT INTO HIST (EMP_ID, NAME ) VALUES (:NEW.EMP_ID , :NEW.NAME ) ;但在删除时,我想使用 :OLD ,而不是单独的为此插入语句。"

        这是一张宽大的桌子。所以?并不是说文本编辑器中没有 REPLACE,您不会再编写 Insert,只需复制、粘贴、选择、将 :NEW 替换为 :OLD。

        Tony 确实有一个解决方案,但我严重怀疑其性能是否优于 2 次插入。

        有什么大不了的?


        编辑

        我要避免的主要事情是在表更改时必须管理 2 次插入。 ——马修·沃森

        我一直在与这种态度作斗争。那些编写 Java 或 C++ 或 .Net 的人有一个内置的 RBO……这样做,这很好。不要那样做,那样不好。他们根据这些规则编写代码,这很好。问题是这些规则何时应用于数据库。数据库的行为方式与代码不同。

        在代码世界中,在两个“地方”拥有本质上相同的代码是不好的。我们避免它。可以将该代码抽象为一个函数并从两个位置调用它,从而避免对其进行两次维护,并且可能会丢失一个,等等。我们都知道这个练习。

        在这种情况下,虽然 true 最后我推荐了两个插入,但它们由 ELSE 分隔。你不会改变一个而忘记另一个。 就在那儿。它不在不同的包中,不在某些已编译的代码中,甚至不在同一个触发器中的其他地方。它们就在彼此旁边,有一个 ELSE,并且插入以 :NEW 重复,而不是 :OLD。为什么我对这个如此疯狂?这里真的有区别吗?我知道两个插入不会比其他想法差,而且可能会更好。

        真正的原因是为重要的时代做好准备。如果您只是为了维护而避免两次插入,那么您将错过这会产生巨大影响的时期。

        INSERT INTO log
        SELECT * FROM myTable 
        WHERE flag = 'TRUE'
        
        ELSE                          -- column omitted for clarity
        
        INSERT INTO log
        SELECT * FROM myTable 
        WHERE flag = 'FALSE'
        

        包括 Matthew 在内的一些人会说这是糟糕的代码,有两个插入。我可以很容易地用绑定变量替换 'TRUE' 和 'FALSE' 并随意翻转它。这就是大多数人会做的事情。但是如果 True 是 0.1% 的值并且 99.9% 是 False,那么您需要两个插入,因为您需要两个执行计划。一个更好用一个索引,另一个用 FTS。所以,是的,您确实需要维护两个插入。这并不总是坏事,在这种情况下它是好的和可取的。

        【讨论】:

        • 我要避免的主要事情是在表更改时必须管理 2 次插入。
        【解决方案5】:

        按照其他人的建议,使用复合触发器。根据需要将旧值或新值保存到变量中,并在插入语句中使用变量:

        declare
          v_col1  table_name.col1%type;
          v_col2  table_name.col2%type;
        begin
          if deleting then
            v_col1 := :old.col1;
            v_col2 := :old.col2;
          else
            v_col1 := :new.col1;
            v_col2 := :new.col2;
          end if;
        
          insert into audit_table(col1, col2)
          values(v_col1, v_col2);
        end;
        

        【讨论】:

        • mm,是的,希望能够只复制记录。那好吧。谢谢
        【解决方案6】:

        创建或替换触发器audit_tgr 在“table_name”上插入、更新或删除之前

        for each row
         begin
          if (inserting or updating) then
           insert into audit table (a,b,c) values(:new.a,:new.b,:new.c);
          else
           insert into audit table (a,b,c) values(:old.a,:old.b,:old.c);
        end;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-11-10
          • 2018-12-19
          • 1970-01-01
          • 2013-09-15
          • 1970-01-01
          • 2023-04-01
          • 2014-02-15
          相关资源
          最近更新 更多