2016-07-27 63 views
0

私の問題を検索しましたが、解決策がまだ見つかりませんでしたので、ここに投稿しています。 Oracle Database 12c Enterprise Editionリリース12.1.0.1.0 - 64bit Productionを使用しています。oracleでCompound Trigger Updateの古い値を取得する方法

私はMain_Tableを10個のレコードと仮定し、新しいテーブルの更新時に古い値と新しい値を記録(挿入)するLog_Tableを持っています。

"Main_Table"のすべての列を動的にループし、新しいものをフィルタリングして更新行レコードを取得する複合トリガを使用しています。プライマリキー(UID))。

私はコンパウンドトリガーを使用しています。

私はすべての列を動的にループしており、列の値を一致させる必要があるため、:old:newを使用していません。

But it is again giving me mutating error: 
Error report - 
SQL Error: ORA-04091: table Main_Table is mutating, trigger/function may not see it 
ORA-06512: at "Schema.TRG_TEST", line 87 
ORA-04088: error during execution of trigger 'Schema.TRG_TEST' 
04091. 00000 - "table %s.%s is mutating, trigger/function may not see it" 
*Cause: A trigger (or a user defined plsql function that is referenced in 
      this statement) attempted to look at (or modify) a table that was 
      in the middle of being modified by the statement which fired it. 
*Action: Rewrite the trigger (or function) so it does not read that table. 


Below is my trigger code: 

create or replace TRIGGER TRG_TEST 
FOR INSERT or UPDATE ON Main_Table 
COMPOUND TRIGGER 
    TYPE NUMBER_TABLE IS TABLE OF NUMBER; 
    tblTABLE2_IDS NUMBER_TABLE; 
    TYPE VARCHAR_TABLE IS TABLE OF VARCHAR(2000); 
    tblTABLE3_IDS VARCHAR_TABLE; 
    TYPE VARCHAR_TABLE1 IS TABLE OF VARCHAR(2000); 
    tblTABLE4_IDS VARCHAR_TABLE1; 

    vcount NUMBER; 
    colCount NUMBER; 
    colCountAfter NUMBER; 
    vvalue VARCHAR2(4000); 
    vcolumn VARCHAR2(4000); 
    sql1 VARCHAR2(4000); 
    dynamicq varchar(4000); 
testv varchar(2000); 
testv1 varchar(2000); 
ssql varchar(2000); 
ssql1 varchar(2000); 
maxsiteid NUMBER; 
newsid varchar(2000); 
newstid varchar(2000); 
newuid varchar(2000); 
--Executed before DML statement 

    BEFORE STATEMENT IS 
    BEGIN 
tblTABLE2_IDS := NUMBER_TABLE(); 
    tblTABLE3_IDS:= VARCHAR_TABLE(); 
    tblTABLE4_IDS:= VARCHAR_TABLE1(); 
    IF UPDATING THEN 
    dbms_output.put_line('Before Statement - In Updating'); 
     --dbms_output.put_line('Before Each Row - In Updating'); 
     -- tblTABLE2_IDS.EXTEND; 
     --tblTABLE2_IDS(tblTABLE2_IDS.LAST) := :new.UID; 
     END IF; 
    END BEFORE STATEMENT; 

    --Executed before each row change- :NEW, :OLD are available 
    BEFORE EACH ROW IS 
    BEGIN 
     IF UPDATING THEN 
     dbms_output.put_line('Before Each Row - In Updating'); 
     tblTABLE2_IDS.EXTEND; 
     tblTABLE2_IDS(tblTABLE2_IDS.LAST) := :new.UID; 
    -- FOR columnlist IN 
     --(SELECT COLUMN_NAME AS COLUMN_NAME FROM all_tab_columns WHERE lower(TABLE_NAME) = 'Main_Table' 
     -- AND lower(COLUMN_NAME) NOT IN ('s_id' ,'msid' ,'st' ,'u_id' ,'db_flag')) 

     --LOOP 
     --colCount:=colCount+1; 
     --ssql1:='select '||columnlist.COLUMN_NAME||' from Main_Table where UID='||tblTABLE2_IDS(tblTABLE2_IDS.LAST)||''; 
     --dbms_output.put_line(ssql1); 
     --execute immediate ssql1 into testv; 
     --tblTABLE3_IDS(colCount):=testv; 
     --dbms_output.put_line(testv); 
     --END LOOP; 
      END IF; 
    END BEFORE EACH ROW; 

--Executed aftereach row change- :NEW, :OLD are available 
    AFTER EACH ROW IS 
    BEGIN 

    IF UPDATING THEN 
     dbms_output.put_line('After Each Row - In Updating'); 
     FOR columnlist IN 
     (SELECT COLUMN_NAME AS COLUMN_NAME FROM all_tab_columns WHERE lower(TABLE_NAME) = 'Main_Table' 
     AND lower(COLUMN_NAME) NOT IN ('s_id' ,'msid' ,'st' ,'u_id' ,'db_flag')) 

     LOOP 
     colCount:=colCount+1; 
     ssql1:='select '||columnlist.COLUMN_NAME||' from Main_Table where UID='||tblTABLE2_IDS(tblTABLE2_IDS.LAST)||''; 
     dbms_output.put_line(ssql1); 
     execute immediate ssql1 into testv; 
     tblTABLE3_IDS(colCount):=testv; 
     dbms_output.put_line(testv); 
     END LOOP; 
      END IF; 
    END AFTER EACH ROW; 

--Executed after DML statement 
    AFTER STATEMENT IS 
    BEGIN 

     IF UPDATING THEN 
      dbms_output.put_line('After Statement - In Updating'); 
      FOR columnlist IN 
     (SELECT COLUMN_NAME AS COLUMN_NAME FROM all_tab_columns WHERE lower(TABLE_NAME) = 'Main_Table' 
     AND lower(COLUMN_NAME) NOT IN ('s_id' ,'msid' ,'st' ,'u_id' ,'db_flag')) 

     LOOP 
     colCountAfter:=colCountAfter+1; 
     dbms_output.put_line('loop started'); 

     ssql1:='select '||columnlist.COLUMN_NAME||' from Main_Table where UID='||tblTABLE2_IDS(tblTABLE2_IDS.LAST)||''; 

     execute immediate ssql1 into testv1; 
     dbms_output.put_line(testv); 
     tblTABLE3_IDS(colCountAfter):=testv1; 

     IF ((testv) IS NOT NULL) THEN 
     FOR i IN tblTABLE3_IDS.FIRST..tblTABLE2_IDS.LAST LOOP 
     dbms_output.put_line('Values No :' ||i||' is ' || tblTABLE3_IDS(i) || ' and ' ||tblTABLE4_IDS(i)); 
     IF(tblTABLE3_IDS(i)=tblTABLE4_IDS(i)) THEN 
     dbms_output.put_line(testv1); 


     ELSE 
     -- dbms_output.put_line('select :new.'|| columnlist.COLUMN_NAME||' from dual'); 
     dbms_output.put_line(testv1);   


     INSERT INTO Log_Table 
       (
       user_id, 
       log_action, 
       log_table_name, 
       schema_name, 
       log_column_name, 
       col_old_val, 
       col_new_val, 
       ne_type, 
       ne_id, 
       system_id 
      ) 
       VALUES 
       (
       newuid, 
       'UPDATE', 
       'Main_Table', 
       'SCHEMA' 
       ,columnlist.COLUMN_NAME 
       ,tblTABLE3_IDS(i) 
       ,tblTABLE4_IDS(i) 
       ,'S' 
       ,newstid 
       ,newsid 
      ); 
     END IF; 
     END LOOP; 


     END IF; 
     END LOOP; 
      END IF; 

    END AFTER STATEMENT; 
    END TRG_TEST; 

は当初、私はその後、私は両方のケースでは「各行の後」、同じエラーでそれにアクセスしようとした「それぞれの行の前に」の更新テーブルにアクセスしようとしました。

私はコンパウンドトリガを使用した後でも解決策を見つけるのに苦労していますが、私は同じことをインサートにも達成しました。

誰でもどのように達成することができますか?前もって感謝します。

+0

データベースのエディションとバージョンを投稿してください。あなたは車輪を再汚染していると思います。同じようなオラクル製品がいくつかあります。 –

+0

Oracle Database 12c Enterprise Editionリリース12.1.0.1.0 - 64bit Productionを使用しています。 – nks

+0

No.いいえ、いいえ。実行時にすべての単一のDML文に対して動的SQL *を生成、コンパイル、実行するこの考え方は本当にひどいです。すべての変更を追跡するためにトリガを使用する必要がある場合は、各テーブルのトリガコードを個別に生成します。あなたは自分自身を苦痛と煩わしさの多くを救うでしょう、私を信じてください。 –

答えて

0

通常、ロギングテーブルを埋めるには、after文の行レベルトリガで十分です。 古い値と新しい値はoldと:newの疑似レコードで使用できます。

非常に基本的な例:

create or replace trigger my_trigger 
    after insert or update on my_table 
    for each row 
declare 

    begin 

     if inserting 
     then 
      insert into logging table 
      (id 
      ,my_column_old 
      ,my_column_new) 
      values 
      (:new.id 
      ,null  -- no old value in case of insert 
      ,:new.my_column); 
     else 
      insert into logging table 
      (id 
      ,my_column_old 
      ,my_column_new) 
      values 
      (:new.id 
      ,:old.my_column 
      ,:new.my_column); 
     end if; 
    end; 
+0

こんにちはルネ、答えてくれてありがとう、私の編集した質問を見てください。私はテーブルの列名を動的に取得しているので、古いものを使うことはできません。 – nks

+0

なぜ動的SQL? – Rene

+0

私はすべての列の値を最初に動的にチェックしてから新しい(all_columns)の値と一致する必要があります。違いがあれば、古い値と新しい値との違いを記録します。 – nks

0

私はあなたが新しい追加された列の履歴を追跡したいあなたのコメントの一つ(ユーザーが列を追加および削除することができます)から理解し、あなたはトリガーを再コンパイルしないで欲しいと。

次に、あなたは、Oracle 12cのEEを(ほとんどのバグが修正されている)持って幸いOracle Total Recall

を使用することができます。

+0

ありがとうございます。理解が必要な新しい概念があります。しかし、今のところ私は時間の制約のためにこれを実装することを検討していないいくつかの理由が原因です。現在、SQLトリガヘルプは私の日を節約します。 – nks

+0

また、私は列自体ではなく、古い列と新しい更新された列の履歴を追跡したい。 – nks

関連する問題