4

トリガーが実行される原因となった実行中の DML (SQL ステートメント) を確認できますか?

たとえば、 INSERT トリガー内でこれを取得したいと思います。

「myTable (名前) 値に挿入 ('Fred')」

このような記事で ora_sql_txt(sql_text) について読みましたが、それを機能させることができませんでした.

Oracle 10 を使用しています。

前もって感謝します。

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

[編集済み] 詳細:既存のデータベース (DB1) を、ネットワーク経由でアクセスできない分類 データベース (DB2) に複製する必要があります。これらのデータベースの同期を保つ必要があります。(DB2) には (DB1) システムに含まれていない追加のテーブルとデータが含まれるため、これは (DB1) から (DB2) への一方向の同期です。

これらのデータベースは稼働状態を維持する必要があるため、データベースを停止せずに同期する方法を決定する必要があります (たとえば、バックアップと復元のために)。そこで、実行中の実際の DML (データが変更されたとき) を保存できれば、新しいデータベースで DML を「再生」して、誰かが手動で入力し直すように更新できると考えました。

データのサイズが非常に大きいため、すべてのデータを引き継ぐことはできません。また、FK 制約と、レコードを挿入/更新する順序のために、変更されたレコードを単にコピーすることもできません。マスターを変更した正確な SQL を使用して、何が起こったかのログを「再生」できれば、データベースの同期を保つことができると考えました。

私の現在の攻撃計画は、変更、挿入、および削除されたすべてのレコードのログを保持することでした。同期したい場合、システムはそれらのレコードを挿入/更新/削除する DML を生成します。次に、.SQL ファイルを機密システムに持っていき、スクリプトを実行します。私が直面している問題は FK です。(DMLを生成するとき、そこに到達するためのパスではなく、データの現在の状態しか知らないため、ステートメントの順序が問題になります)。すべての FK を無効にし、マージを実行してから、すべての FK を再度有効にできると思います...

だから - 実際の DML を保存するという私のアプローチは池の水を吸いますか、それともより良い解決策がありますか?

4

4 に答える 4

2

「実際の DML を保存するという私のアプローチは、池の水を吸いますか?」はい..

  1. DB1 での DML の厳密な順序付けは、実際には存在しません。複数のプロセス、複数のコア、本質的に同時に起こっていること。

  2. そして、DML は、連続して発生した場合でも、そのようには機能しません。次の 2 つの更新ステートメントが、トランザクション 1 がコミットされる前にトランザクション 2 の更新が開始される、個別のトランザクションを使用して個別のプロセスで実行されるとします。

     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
    

最初のトランザクションで行われた変更は2 番目のトランザクションには表示されないため、2 番目のトランザクションによって変更された行には、最初のトランザクションの行は含まれません。ただし、DML を取得して順次再生すると、トランザクション 1 の変更が表示されるため、トランザクション 2 の変更は異なります。( Tom Kyte のExpert Oracle Database Architecture Second Editionの 40 ページと 41 ページを参照してください。)

  1. うまくいけば、バインド変数を使用しているので、DML だけでは意味がありませんupdate table_a set col_a = :col_a where id = :id。では、変数バインディングを含む DML が必要です。

  2. シーケンスを使用しますか? その場合、next_val はDB1 と DB2 の間で同期されません。(たとえば、インスタンスの障害によって値が失われる可能性があります。両方のシステムで同時に障害が発生しますか?) また、ノードによって next_val が異なる RAC を扱っている場合は、忘れてください。

まず、Oracle のレプリケーションを調査することから始めます。

于 2010-08-18T20:51:49.547 に答える
2

メタデータ/構成の変更 (少数のテーブルに格納されている) を開発環境からテスト後に本番環境に移動する必要がある状況がありました。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;

*/
于 2018-01-10T22:09:46.003 に答える
1

この関数は、ここで説明するように、「イベント」トリガーに対してのみ機能します。このためのメカニズムとして、きめ細かい監査を検討する必要があります。詳細はこちら

于 2010-08-18T01:10:03.823 に答える
1

トリガー コードが実行されたときに、実行の原因となった dml がわからないのですか?

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

この場合、それは emp_tab テーブルに対する挿入または更新ステートメントである必要があります。

更新か挿入かを調べるには

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

正確な列の値は、:old および :new 疑似列で使用できます。

于 2010-08-18T05:57:34.630 に答える