2010-08-17 64 views
2

是否有可能看到正在運行的DML(SQL語句)導致觸發器被執行?我可以在Oracle觸發器中看到DML嗎?

例如,INSERT觸發器中我想獲得這樣的:

「插入到mytable的(名稱)VALUES( '弗雷德')」

我讀到此類文章ora_sql_txt(SQL_TEXT)作爲this,但無法讓它工作 - 不知道這是否甚至導致我沿着正確的道路?

我們正在使用Oracle 10.

在此先感謝您。

=========================

將帖子更多細節:我們需要複製一個現有的數據庫(DB1)轉換爲可通過網絡訪問的分類數據庫(DB2)而不是。我需要保持這些數據庫同步。這是從(DB1)到(DB2)的單向同步,因爲(DB2)將包含未包含在(DB1)系統中的附加表和數據。

我必須確定一種同步這些數據庫的方法,而不必將它們關閉(比如說備份和恢復),因爲它需要保持活動狀態。所以我認爲,如果我可以存儲正在運行的實際DML(當數據發生變化時),我可以在新數據庫上「回放」DML以更新它,就像有人手動輸入它一樣。

由於FK的限制以及我插入/更新記錄的順序,我無法複製所有數據,並且我不能僅複製已更改的記錄。我認爲如果我能夠「回放」所發生事件的日誌,使用改變主服務器的確切SQL,我可以保持數據庫同步。

我目前的攻擊計劃是記錄所有已更改,插入和刪除的記錄,並且當我想同步時,系統會生成DML以插入/更新/刪除這些記錄。然後,我將.SQL文件帶到分類系統並運行腳本。我遇到的問題是FK。 (因爲當我生成DML時,我只知道數據的當前狀態,而不是它的路徑 - 因此語句的排序是一個問題)。我想我可以禁用所有FK的,做合併,然後重新啓用所有FK的...

所以 - 我的方法來存儲實際的DML,它會發生吸池水,還是有更好的解決方案嗎? ??

+0

這是我目前的方向:根據需求,我收集插入/修改/刪除記錄的列表,並生成相應的SQL到文件。此外,該文件包含禁用約束的正確命令,並在完成時重新啓用它們。 然後在目標服務器上(不在網絡上),我只需從拇指驅動器運行更新腳本。似乎工作到目前爲止...... – BigWorld 2010-08-19 19:44:56

+0

因爲看起來並不是真的有可能抓住觸發器中的DML(除非它是一個系統事件),我決定使用我在上面評論中提到的方法來解決我的問題。正如下面提到的,第三個pary複製工具也可以爲我生成更改腳本,但預算限制使得這個小項目不會發生。感謝大家在我的第一個StackOverflow問題上的幫助 - 希望我做對了! – BigWorld 2010-08-20 16:00:24

回答

0

該功能僅適用於'事件'觸發器,如討論here。 你應該看看Fine-Grained Auditing作爲一個機制。詳細信息here

+0

謝謝加里 - 也許審計是我想去的地方,但我被告知我不想在生產系統中保留這個。我將在我的問題下添加一條評論,詳細解釋我正在努力完成的任務。 – BigWorld 2010-08-18 16:32:39

+0

在這種情況下,您不僅捕獲SQL,還會綁定變量和可能的會話狀態。按照Shannon的建議,考慮實體化視圖,Oracle複製(例如Streams,GoldenGate)或第三方複製解決方案。不要試圖建立自己的。 – 2010-08-19 01:06:37

+0

感謝加里 - 我目前正在深入閱讀Oracle複製。也許有一些我錯過了,但我是一個SQL Server DBA,所以這對我來說大部分都是希臘語。是否有任何這些複製解決方案(您知道)提供一個選項來爲不在網絡上的服務器生成某種增量文件? 謝謝! – BigWorld 2010-08-19 19:48:57

1

當觸發器代碼運行時,您不知道導致它運行的dml嗎?

CREATE OR REPLACE TRIGGER Print_salary_changes 
     BEFORE INSERT OR UPDATE ON Emp_tab 
     FOR EACH ROW 
     ... 

在這種情況下,它必須是emp_tab表上的insert或update語句。

要了解,如果它是一個更新或插入

if inserting then 
... 
elsif updating then 
... 
end if; 

確切的列值在可用:老:新的僞列。

+0

其實,我不知道是什麼聲明導致了它被解僱 - 這正是我想要弄髒我的骯髒的小手指。我知道我可以看到哪些字段發生了變化(如上面的示例所示),但是我想知道*如果*我發現正在運行的SQL語句導致觸發器觸發。我將在我的問題下添加註釋,以解釋更詳細地說明了我正在努力完成的任務,但如果有一個簡單的話,我不想過於詳細年。謝謝Rene! – BigWorld 2010-08-18 16:31:21

+0

此外,你是正確的 - 我* *可以*生成適當的SQL使用:舊/:新僞列知道它是否是一個插入/更新,但基於純粹的表和列,以及代碼量'我必須寫和維護,我想我只是試圖抓住實際的SQL,因爲它已經爲我完成了。 ;) – BigWorld 2010-08-18 17:11:57

1

「我的方法是將實際的DML存儲爲真實吸水池水?」是的。

  1. DB1上DML的嚴格排序並不存在。多進程,多核,基本上同時發生的事情。

  2. 而DML即使在順序發生時也不會像這樣。下面要說的兩個更新語句與單獨的交易,其中交易的更新交易前2點開始1個提交單獨的進程中運行:

    update table_a set col_a = 10 where col_b = 'A' -- transaction 1 
    update table_a set col_c = 'Error' where col_a = 10 -- transaction 2 
    

由於在第一個事務中所做的更改是不visibible到第二個事務中,第二個事務更改的行將不包含第一個事務的行。但是,如果您設法捕捉DML並按順序重放,則事務1的更改將可見,因此事務2的更改將爲不同。 (見40和41 Tom Kyte's Expert Oracle Database Architecture Second Edition頁。)

  • 希望您使用綁定變量,所以DML本身沒有意義:update table_a set col_a = :col_a where id = :id現在怎麼辦?好吧,所以你想要DML和它的變量綁定。

  • 你使用序列嗎?如果是這樣,next_val將而不是在DB1和DB2之間保持同步。 (例如,實例故障可能導致值丟失,兩個系統都會同時失敗?)如果您正在處理RAC,其中next_val因節點而異,請將其忽略。

  • 我會從調查Oracle的replication開始。

    +0

    瞭解。我們已經想到了幾個這樣的項目,比如序列。由於這是單向同步,並且目標數據庫是「只讀」,所以它不會成爲問題。 關於您對Oracle複製的評論 - 它是否支持複製到不在網絡上的數據庫?換句話說,有人需要手提數據並手動加載數據(非技術人員)。這是我最大的限制 - 它需要在獨立的服務器上由非技術人員執行,而不必關閉服務器,並且不擦除該系統特有的附加數據。 :(謝謝Shannon! – BigWorld 2010-08-19 19:39:21

    +0

    如果你正在使用DML,目標數據庫不是「只讀」,我的意思是,如果你運行的DML包含像'insert ... values(sequence_X.nextval')將獲得適用於目標的'sequence_x.nextval'的值,這可能與源不同。 對不起,但我沒有任何關於複製的經驗,我只知道它在那裏,因此「我會開始調查......「除了流複製之外,還有高級複製 目標數據庫包含除要複製的表之外的數據嗎? – 2010-08-19 20:19:30

    +0

    正確,這就是爲什麼我用雙引號」只讀「的原因:) - 對於*用戶*是隻讀的,換句話說,在該服務器上不會生成新的數據,並且由於我插入的是主服務器上生成的主鍵,所以一個序列永遠不會在目標上使用。 (真相被告知,只讀實例甚至沒有創建序列)。感謝您的所有評論香農 - 我在此過程中學到了一些東西,今天將會結束這個問題。 – BigWorld 2010-08-20 15:28:53

    0

    我遇到了這樣的情況:我需要將元數據/配置更改(存儲在少數表中)從開發環境移動到生產環境,一旦測試完成。像Goldengate這樣的產品就是用來做這件事的產品,但這可能是昂貴且複雜的設置和管理。

    以下過程生成觸發器並將其附加到需要保存DML的表。觸發器重新創建DML,並在下面的情況下將其保存到審計表 - 它取決於您如何處理它。您可以使用保存在審計表中的語句重播來自給定時間點的更改(剪切並粘貼或開發將其應用於目標的過程)。

    希望你覺得這個有用。

    procedure gen_trigger(p_tname in varchar2) 
    is 
        l_theCursor  integer default dbms_sql.open_cursor; 
        l_query   varchar2(1000) default 'select * from ' || p_tname; 
        l_colCnt  number := 0; 
        l_descTbl  dbms_sql.desc_tab; 
        trg    varchar(32767) := null; 
        expr   varchar(32767) := null; 
        cmd    varchar(32767) := null; 
    
    begin 
    
        dbms_sql.parse( l_theCursor, l_query, dbms_sql.native); 
        dbms_sql.describe_columns(l_theCursor, l_colCnt, l_descTbl); 
    
        trg := q'# 
         create or replace trigger <%TABLE_NAME%>_audit 
         after insert or update or delete on <%TABLE_NAME%> for each row 
         declare 
         qs varchar2(20) := q'[q'^]'; 
         qe varchar2(20) := q'[^']'; 
         command clob; 
         nlsd  varchar2(100); 
         begin 
          select value into nlsd from nls_session_parameters where parameter = 'NLS_DATE_FORMAT'; 
          execute immediate 'alter session set nls_date_format = ''YYYY/MM/DD hh24:mi:ss'' '; 
          if inserting then 
           command := <%INSERT_COMMAND%>; 
          end if; 
          if updating then 
           command := <%UPDATE_COMMAND%>; 
          end if; 
          if deleting then 
           command := <%DELETE_COMMAND%>; 
          end if; 
          insert into x_audit values (systimestamp, command); 
          execute immediate q'+alter session set nls_date_format = '+'|| nlsd || q'+'+'; 
         end; 
        #'; 
    
        -- Create the insert command 
        cmd := q'#'insert into <%TABLE_NAME%> (<%INSERT_COLS%>) values ('||<%INSERT_VAL%>||')'#'; 
        -- columns clause 
        for i in 1 .. l_colCnt loop 
         if expr is not null then 
          expr := expr || ','; 
         end if; 
         expr := expr || l_descTbl(i).col_name; 
        end loop; 
        cmd := replace(cmd,'<%INSERT_COLS%>',expr); 
    
        -- values clause 
        expr := null; 
        for i in 1 .. l_colCnt loop 
         if expr is not null then 
          expr := expr || q'#||','||#'; 
         end if; 
         expr := expr || 'qs||:new.' || l_descTbl(i).col_name || '||qe'; 
        end loop; 
        cmd := replace(cmd,'<%INSERT_VAL%>',expr); 
        trg := replace(trg,'<%INSERT_COMMAND%>',cmd); 
    
        -- create the update command 
        -- set clause 
        expr := null; 
        cmd := q'#'update <%TABLE_NAME%> set '||<%UPDATE_COLS%>||' where '||<%WHERE_CLAUSE%>#'; 
        for i in 1 .. l_colCnt loop 
         if expr is not null then 
          expr := expr || q'#||','||#'; 
         end if; 
         expr := expr || q'#'#' || l_descTbl(i).col_name || q'# = '||#'|| 'qs||:new.'||l_descTbl(i).col_name || '||qe'; 
        end loop; 
        null; 
        cmd := replace(cmd,'<%UPDATE_COLS%>',expr); 
        trg := replace(trg,'<%UPDATE_COMMAND%>',cmd); 
    
        -- create the delete command 
        expr := null; 
        cmd := q'#'delete <%TABLE_NAME%> where '||<%WHERE_CLAUSE%>#'; 
        trg := replace(trg,'<%DELETE_COMMAND%>',cmd); 
    
        -- where clause using primary key columns (used by update and delete) 
        expr := null; 
        for pk in (SELECT column_name FROM all_cons_columns WHERE constraint_name = (
            SELECT constraint_name FROM user_constraints 
            WHERE UPPER(table_name) = UPPER(p_tname) AND CONSTRAINT_TYPE = 'P' 
           )) loop 
    
         if expr is not null then    
          expr := expr || q'#|| ' and '||#'; 
         end if; 
    
         expr := expr || q'#'#' || pk.column_name || q'# = '||#'|| 'qs||:old.'|| pk.column_name || '||qe'; 
        end loop; 
        if expr is null then -- must have a primary key 
         raise_application_error(-20000,'The table must have a primary key defined'); 
        end if; 
    
        trg := replace(trg,'<%WHERE_CLAUSE%>',expr); 
    
        trg := replace(trg,'<%TABLE_NAME%>',p_tname); 
    
        execute immediate trg; 
    
        null; 
    
    exception 
        when others then 
         execute immediate 'alter session set nls_date_format=''YYYY/MM/DD'' '; 
         raise; 
    end; 
    
    /* Example 
    
    create table t1 (
    col1 varchar2(100), 
    col2 number, 
    col3 date, 
    constraint pk_t1 primary key (col1) 
    ) 
    /
    
    BEGIN 
        GEN_TRIGGER('T1'); 
    END; 
    /
    
    -- Trigger generated .... 
    
    create or replace trigger t1_audit after 
        insert or 
        update or 
        delete on t1 for each row 
    declare 
        qs  varchar2(20) := q'[q'^]'; 
        qe  varchar2(20) := q'[^']'; 
        command clob; 
        nlsd varchar2(100); 
    begin 
        select value into nlsd from nls_session_parameters where parameter = 'NLS_DATE_FORMAT'; 
        execute immediate 'alter session set nls_date_format = ''YYYY/MM/DD hh24:mi:ss'' '; 
        if inserting then 
         command := 'insert into T1 (COL1,COL2,COL3) values ('||qs||:new.col1||qe||','||qs||:new.col2||qe||','||qs||:new.col3||qe||')'; 
        end if; 
        if updating then 
         command := 'update T1 set '||'COL1 = '||qs||:new.col1||qe||','||'COL2 = '||qs||:new.col2||qe||','||'COL3 = '||qs||:new.col3||qe||' where '||'COL1 = '||qs||:old.col1||qe; 
        end if; 
        if deleting then 
         command := 'delete T1 where '||'COL1 = '||qs||:old.col1||qe; 
        end if; 
        insert into x_audit values 
         (systimestamp, command 
         ); 
        execute immediate q'+alter session set nls_date_format = '+'|| nlsd || q'+'+';    
    end; 
    
    */ 
    
    相關問題