是否有可能设计一个修改相同表(保证不相同行)的Oracle触发器?

是否有可能设计一个修改相同表(保证不相同行)的Oracle触发器?,oracle,plsql,triggers,Oracle,Plsql,Triggers,我需要编写一个触发器,通过将旧值插入或更新到同一表中的另一行,在更新列之前保留该列的旧值。(是的,我知道)。 下面的合并/双重欺骗对我很有用, 但因为在这种情况下,我要插入或更新同一个表, Oracle在运行时抱怨。此外,出于某种原因,我发现编写编译时没有错误的代码异常困难 两个问题: 即使我可以保证触发器永远不会更新触发触发器的行,也有可能修改触发器所在的同一个表吗?或者我必须做一些类似的事情(例如):将挂起的更改插入到另一个表中,以便第二个触发器可以将它们合并回原始表中?(此表是一个客户界面

我需要编写一个触发器,通过将旧值插入或更新到同一表中的另一行,在更新列之前保留该列的旧值。(是的,我知道)。 下面的合并/双重欺骗对我很有用, 但因为在这种情况下,我要插入或更新同一个表, Oracle在运行时抱怨。此外,出于某种原因,我发现编写编译时没有错误的代码异常困难

两个问题:

  • 即使我可以保证触发器永远不会更新触发触发器的行,也有可能修改触发器所在的同一个表吗?或者我必须做一些类似的事情(例如):将挂起的更改插入到另一个表中,以便第二个触发器可以将它们合并回原始表中?(此表是一个客户界面,因此我无法重新构建此表以使用第二个表永久存储旧值。)
  • 不允许我在MERGE语句中使用:old.event\u key,但允许我在MERGE语句中使用:old.property\u val的编译器错误是怎么回事?(声明变量old\u event\u key并将其分配给:old.event\u key的值似乎有效)是否存在某种隐藏的中间语言,可以知道列何时(属于)主键,并阻止您通过:old引用它。
  • 以下是违规代码:

    create or replace trigger remember_old_status
      before update on event_properties
      for each row
        when (old.property_name = 'CURRENT-STATUS')
        declare
          old_event_key varchar2(20);
        begin
          old_event_key := :old.event_key;
          merge into event_properties eprop
            using (select 1 from dual) dummy
            on (    eprop.event_key = old_event_key
                AND eprop.property_name = 'PREVIOUS-STATUS')
            when matched then
              update set property_val = :old.property_val
            when not matched then
              insert (event_key, property_name, property_val)
              values (old_event_key, 'PREVIOUS-STATUS', :old.property_val);
        end;
    
    这是表格:

    CREATE TABLE "CUST"."EVENT_PROPERTIES" 
     (  "EVENT_KEY" VARCHAR2(20 BYTE) CONSTRAINT "NN_FLE_FLK" NOT NULL ENABLE, 
      "PROPERTY_NAME" VARCHAR2(20 BYTE) CONSTRAINT "NN_FLE_PN" NOT NULL ENABLE, 
      "PROPERTY_VAL" VARCHAR2(80 BYTE), 
       CONSTRAINT "PX_EVENT_PROPERTIES" PRIMARY KEY ("EVENT_KEY", "PROPERTY_NAME") DEFERRABLE
    USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
    STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
    PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
    BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
    TABLESPACE "CUST_TS"  ENABLE
     ) SEGMENT CREATION IMMEDIATE 
    PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 
    NOCOMPRESS LOGGING
    STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
    PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
    BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
    TABLESPACE "CUST_TS" ;
    
    以下是错误消息:

    ORA-04091:表CUST.EVENT_属性正在变化,触发器/函数可能看不到它 ORA-06512:在第5行的“客户记住旧的状态”

    您可以使用将旧值存储在每一行部分之前的
    变量中,然后在
    之后的语句
    中合并

    create or replace trigger remember_old_status
    for update on event_properties
    compound trigger
    
      type t_type is table of event_properties%rowtype;
      old_recs t_type := t_type();
    
      before each row is
      begin
        if (:old.property_name = 'CURRENT-STATUS') then
          old_recs.extend();
          old_recs(old_recs.count).event_key := :old.event_key;
          old_recs(old_recs.count).property_name := :old.property_name;
          old_recs(old_recs.count).property_val := :old.property_val;
        end if;
      end before each row;
    
      after statement is
      begin
        forall i in old_recs.first..old_recs.last
          merge into event_properties eprop
            using (
              select old_recs(i).event_key as event_key, 
                'PREVIOUS-STATUS' as property_name,
                old_recs(i).property_val as property_val
                from dual
            ) dummy
            on (eprop.event_key = dummy.event_key
                and eprop.property_name = dummy.property_name)
            when matched then
              update set property_val = old_recs(i).property_val
            when not matched then
              insert (event_key, property_name, property_val)
              values (dummy.event_key, dummy.property_name, dummy.property_val);
      end after statement;
    end remember_old_status;
    /
    
    这假设您一次只更新一行:

    create or replace trigger remember_old_status
    for update on event_properties
    compound trigger
    
      old_rec event_properties%rowtype;
    
      before each row is
      begin
        if (:old.property_name = 'CURRENT-STATUS') then
          old_rec.event_key := :old.event_key;
          old_rec.property_name := :old.property_name;
          old_rec.property_val := :old.property_val;
        end if;
      end before each row;
    
      after statement is
      begin
        if (old_rec.property_name = 'CURRENT-STATUS') then
          merge into event_properties eprop
            using (
              select old_rec.event_key as event_key, 
                'PREVIOUS-STATUS' as property_name,
                old_rec.property_val as property_val
                from dual
            ) dummy
            on (eprop.event_key = dummy.event_key
                and eprop.property_name = dummy.property_name)
            when matched then
              update set property_val = old_rec.property_val
            when not matched then
              insert (event_key, property_name, property_val)
              values (dummy.event_key, dummy.property_name, dummy.property_val);
        end if;
      end after statement;
    end remember_old_status;
    /
    
    快速测试:

    insert into event_properties values('SOME_EVENT', 'CURRENT-STATUS', 'A');
    
    1 row inserted.
    
    update event_properties set property_val = 'B' where event_key = 'SOME_EVENT' and property_name = 'CURRENT-STATUS';
    
    1 row updated.
    
    select * from event_properties;
    
    EVENT_KEY            PROPERTY_NAME        PROPERTY_VAL                                                                    
    -------------------- -------------------- --------------------------------------------------------------------------------
    SOME_EVENT           CURRENT-STATUS       B                                                                               
    SOME_EVENT           PREVIOUS-STATUS      A                                                                               
    
    update event_properties set property_val = 'C' where event_key = 'SOME_EVENT' and property_name = 'CURRENT-STATUS';
    
    1 row updated.
    
    select * from event_properties;
    
    EVENT_KEY            PROPERTY_NAME        PROPERTY_VAL                                                                    
    -------------------- -------------------- --------------------------------------------------------------------------------
    SOME_EVENT           CURRENT-STATUS       C                                                                               
    SOME_EVENT           PREVIOUS-STATUS      B                                                                               
    
    如果要处理一条语句中的多个更新,则每行之前的
    可以填充一个集合,然后可以在
    之后的
    语句中使用该集合

    create or replace trigger remember_old_status
    for update on event_properties
    compound trigger
    
      type t_type is table of event_properties%rowtype;
      old_recs t_type := t_type();
    
      before each row is
      begin
        if (:old.property_name = 'CURRENT-STATUS') then
          old_recs.extend();
          old_recs(old_recs.count).event_key := :old.event_key;
          old_recs(old_recs.count).property_name := :old.property_name;
          old_recs(old_recs.count).property_val := :old.property_val;
        end if;
      end before each row;
    
      after statement is
      begin
        forall i in old_recs.first..old_recs.last
          merge into event_properties eprop
            using (
              select old_recs(i).event_key as event_key, 
                'PREVIOUS-STATUS' as property_name,
                old_recs(i).property_val as property_val
                from dual
            ) dummy
            on (eprop.event_key = dummy.event_key
                and eprop.property_name = dummy.property_name)
            when matched then
              update set property_val = old_recs(i).property_val
            when not matched then
              insert (event_key, property_name, property_val)
              values (dummy.event_key, dummy.property_name, dummy.property_val);
      end after statement;
    end remember_old_status;
    /
    
    您可以使用将旧值存储在每一行
    部分之前的
    变量中,然后在
    之后的语句
    中合并

    create or replace trigger remember_old_status
    for update on event_properties
    compound trigger
    
      type t_type is table of event_properties%rowtype;
      old_recs t_type := t_type();
    
      before each row is
      begin
        if (:old.property_name = 'CURRENT-STATUS') then
          old_recs.extend();
          old_recs(old_recs.count).event_key := :old.event_key;
          old_recs(old_recs.count).property_name := :old.property_name;
          old_recs(old_recs.count).property_val := :old.property_val;
        end if;
      end before each row;
    
      after statement is
      begin
        forall i in old_recs.first..old_recs.last
          merge into event_properties eprop
            using (
              select old_recs(i).event_key as event_key, 
                'PREVIOUS-STATUS' as property_name,
                old_recs(i).property_val as property_val
                from dual
            ) dummy
            on (eprop.event_key = dummy.event_key
                and eprop.property_name = dummy.property_name)
            when matched then
              update set property_val = old_recs(i).property_val
            when not matched then
              insert (event_key, property_name, property_val)
              values (dummy.event_key, dummy.property_name, dummy.property_val);
      end after statement;
    end remember_old_status;
    /
    
    这假设您一次只更新一行:

    create or replace trigger remember_old_status
    for update on event_properties
    compound trigger
    
      old_rec event_properties%rowtype;
    
      before each row is
      begin
        if (:old.property_name = 'CURRENT-STATUS') then
          old_rec.event_key := :old.event_key;
          old_rec.property_name := :old.property_name;
          old_rec.property_val := :old.property_val;
        end if;
      end before each row;
    
      after statement is
      begin
        if (old_rec.property_name = 'CURRENT-STATUS') then
          merge into event_properties eprop
            using (
              select old_rec.event_key as event_key, 
                'PREVIOUS-STATUS' as property_name,
                old_rec.property_val as property_val
                from dual
            ) dummy
            on (eprop.event_key = dummy.event_key
                and eprop.property_name = dummy.property_name)
            when matched then
              update set property_val = old_rec.property_val
            when not matched then
              insert (event_key, property_name, property_val)
              values (dummy.event_key, dummy.property_name, dummy.property_val);
        end if;
      end after statement;
    end remember_old_status;
    /
    
    快速测试:

    insert into event_properties values('SOME_EVENT', 'CURRENT-STATUS', 'A');
    
    1 row inserted.
    
    update event_properties set property_val = 'B' where event_key = 'SOME_EVENT' and property_name = 'CURRENT-STATUS';
    
    1 row updated.
    
    select * from event_properties;
    
    EVENT_KEY            PROPERTY_NAME        PROPERTY_VAL                                                                    
    -------------------- -------------------- --------------------------------------------------------------------------------
    SOME_EVENT           CURRENT-STATUS       B                                                                               
    SOME_EVENT           PREVIOUS-STATUS      A                                                                               
    
    update event_properties set property_val = 'C' where event_key = 'SOME_EVENT' and property_name = 'CURRENT-STATUS';
    
    1 row updated.
    
    select * from event_properties;
    
    EVENT_KEY            PROPERTY_NAME        PROPERTY_VAL                                                                    
    -------------------- -------------------- --------------------------------------------------------------------------------
    SOME_EVENT           CURRENT-STATUS       C                                                                               
    SOME_EVENT           PREVIOUS-STATUS      B                                                                               
    
    如果要处理一条语句中的多个更新,则每行之前的
    可以填充一个集合,然后可以在
    之后的
    语句中使用该集合

    create or replace trigger remember_old_status
    for update on event_properties
    compound trigger
    
      type t_type is table of event_properties%rowtype;
      old_recs t_type := t_type();
    
      before each row is
      begin
        if (:old.property_name = 'CURRENT-STATUS') then
          old_recs.extend();
          old_recs(old_recs.count).event_key := :old.event_key;
          old_recs(old_recs.count).property_name := :old.property_name;
          old_recs(old_recs.count).property_val := :old.property_val;
        end if;
      end before each row;
    
      after statement is
      begin
        forall i in old_recs.first..old_recs.last
          merge into event_properties eprop
            using (
              select old_recs(i).event_key as event_key, 
                'PREVIOUS-STATUS' as property_name,
                old_recs(i).property_val as property_val
                from dual
            ) dummy
            on (eprop.event_key = dummy.event_key
                and eprop.property_name = dummy.property_name)
            when matched then
              update set property_val = old_recs(i).property_val
            when not matched then
              insert (event_key, property_name, property_val)
              values (dummy.event_key, dummy.property_name, dummy.property_val);
      end after statement;
    end remember_old_status;
    /
    

    请看以下示例:。其中一个选项显示了如何使用“另一个表”(您提到的),这是一个全局临时表,而第二个选项演示了在包变量中使用集合。任何对Oracle文档的检查或在
    ORA-04091
    上搜索此站点都会清楚地给出“否”的答案。不能对每行触发的表执行DML。解决这个问题的模式是对每一行使用
    更新另一个表。然后让一个
    after
    语句级触发器使用另一个表中的信息来更新被触发的表。在问题的第二部分中,您指的是什么编译器错误?直接在代码中使用
    old.event\u键
    可以编译OK。好吧,不管怎样,在一个问题中问两个问题是没有帮助的;如果您仍然可以重现实际代码和错误,那么您应该单独问一个问题。请看以下示例:。其中一个选项显示了如何使用“另一个表”(您提到的),这是一个全局临时表,而第二个选项演示了在包变量中使用集合。任何对Oracle文档的检查或在
    ORA-04091
    上搜索此站点都会清楚地给出“否”的答案。不能对每行触发的表执行DML。解决这个问题的模式是对每一行使用
    更新另一个表。然后让一个
    after
    语句级触发器使用另一个表中的信息来更新被触发的表。在问题的第二部分中,您指的是什么编译器错误?直接在代码中使用
    old.event\u键
    可以编译OK。好吧,不管怎样,在一个问题中问两个问题是没有帮助的;如果您仍然可以重现代码和错误,那么您应该单独询问显示实际代码和错误的问题。