2016-07-27 20 views
0

Ich suchte nach meinem Problem, konnte aber noch keine Lösung bekommen, also poste ich es hier. Ich verwende Oracle Database 12c Enterprise Edition Version 12.1.0.1.0 - 64-Bit-Produktion.Wie bekomme ich alte Werte in Compound Trigger Update in Oracle

Ich habe eine Main_Table mit annehmen 10 Datensätze und ich habe eine Log_Table, um die alten und neuen Werte bei der Aktualisierung der neuen Tabelle zu protokollieren (einzufügen).

Ich verwende einen Compound-Trigger (um Mutationsfehler zu vermeiden), um alle Spalten von "Main_Table" dynamisch zu durchlaufen und die aktualisierenden Zeileneinträge durch Filtern durch neue zu erhalten. Primärschlüssel (UID)).

Ich hoffe, ich verwende Compound Trigger richtig.

Ich verwende nicht :old und :new, da ich alle Spalten dynamisch durchlaufen muss und die Spaltenwerte übereinstimmen muss.

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; 

Anfangs habe ich versucht hatte, in die Aktualisierung Tabelle zugreifen „Vor jeder Zeile“ dann habe ich versucht, es in „Nach jeder Zeile“, gleichen Fehler in beiden Fällen erreichbar.

Ich habe Mühe, eine Lösung zu finden, auch nach der Verwendung von Compound-Trigger, aber ich habe das gleiche für den Einsatz erreicht.

Kann jemand helfen, wie man es erreicht. Danke im Voraus.

+0

Bitte veröffentlichen Sie die Datenbankversion und -version. Ich nehme an, du hast das Rad wiederbelebt. Es gibt einige Oracle-Produkte, die das gleiche tun. –

+0

Ich verwende Oracle Database 12c Enterprise Edition Version 12.1.0.1.0 - 64-Bit-Produktion. – nks

+0

Nein. Nur, nein. Diese Idee, dynamisches SQL * für jede einzelne DML-Anweisung zur Laufzeit zu erzeugen, zu kompilieren und auszuführen, ist wirklich schrecklich. Wenn Sie alle Änderungen mit einem Trigger verfolgen müssen, generieren Sie Ihren Triggercode für jede Tabelle einzeln. Sie werden sich eine Menge Schmerzen und Ärger ersparen, glauben Sie mir. –

Antwort

0

Normalerweise reicht ein Trigger nach Zeilenanweisung aus, um eine Protokolltabelle zu füllen. Alte und neue Spaltenwerte sind in den alten und neuen Pseudodatensätzen verfügbar.

Ein sehr einfaches Beispiel:

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

Hallo Rene, Danke für die Antwort, bitte schauen Sie sich meine bearbeitete Frage an. Ich kann nicht neu und alt verwenden, da ich die Tabellenspaltennamen dynamisch erhalte. – nks

+0

warum dynamische sql? – Rene

+0

Ich muss zuerst alle Spaltenwerte dynamisch überprüfen und dann mit neuen (all_columns) Werten übereinstimmen, wenn es einen Unterschied gibt, dann soll ich diesen Unterschied mit alten und neuen Werten protokollieren. Ich kenne die Spaltennamen zur Laufzeit nicht. – nks

0

Wie ich von einem Ihrer Kommentare verstehen Sie Geschichte neu hinzugefügten Spalte verfolgen möchten (Benutzer können Spalte hinzufügen und Drop), und Sie wollen nicht Trigger neu kompilieren.

Dann können Sie Oracle Total Recall

verwenden Zum Glück haben Sie Oracle 12c EE (die meisten Fehler beseitigt).

+0

Thanks.Ich sehe ein neues Konzept, das Verständnis erfordert, ich werde schauen in es, aber aus irgendeinem Grund bin ich nicht auf der Suche nach dies aufgrund der Zeitbeschränkung. Derzeit, A sql Trigger Hilfe wird meinen Tag retten. – nks

+0

Außerdem möchte ich den Verlauf jeder Spalte alte und neue aktualisierte Werte nicht die Spalten selbst verfolgen. – nks