2016-07-27 11 views
0

내 문제를 조사했지만 해결 방법이 아직 없으므로 여기에 게시 할 예정입니다. Oracle Database 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production을 사용하고 있습니다.오라클의 Compound Trigger Update에서 오래된 값을 얻는 방법

나는 Main_Table에 10 개의 레코드가 있다고 가정하고 새로운 테이블을 업데이트 할 때 이전 값과 새 값을 로그 (삽입)하는 Log_Table이 있습니다.

나는 "Main_Table"의 모든 열을 동적으로 반복하고 new를 통해 필터링하여 업데이트하는 행 레코드를 얻기 위해 복합 트리거 (변형 오류를 피하기 위해)를 사용하고 있습니다. 기본 키 (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 - 64 비트 생산을 사용하고 있습니다. – nks

+0

아니, 그냥. 런타임에 모든 단일 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

(사용자 추가 및 열을 삭제할 수 있습니다) 당신은 트리거를 다시 컴파일하지합니다.

그럼 당신은 당신이 오라클 12C EE를 (대부분의 버그가 수정 된)이 행복하게 Oracle Total Recall

를 사용할 수 있습니다.

+0

고맙습니다. 이해가 필요한 새로운 개념을 보았습니다. 그것으로,하지만 지금은 시간 제약으로 인해이 구현을보고되지 않는 몇 가지 이유로 인해 지금. 현재, SQL 트리거 도움이 하루를 저장합니다. – nks

+0

또한, 나는 열 자체가 아닌 이전 열과 새로운 업데이트 된 열의 기록을 추적하려고합니다. – nks