4

テーブルを監査するためのtriggerinを作成する必要があります。oracle 11g

50 columns私はそれが必要なテーブルを持っていますaudited

  • テーブルに入れるにはevery new insert、にエントリを入れる必要がありますaudit table (1 row)
  • たとえばevery update、更新する1st 2nd columnと、監査で2つのレコードが作成されます old value and new value

監査テーブルの構造は

 id        NOT NULL
 attribute NOT NULL
 OLD VALUE NOT NULL
 NEW VALUE NOT NULL
 cre_date  NOT NULL
 upd_date  NULL
 cre_time  NOT NULL
 upd_time  NULL

の場合insert、主キー(メインテーブル)のみを入力する必要があります。更新の場合、colAとcolBが更新されていると仮定すると、すべてを入力する必要があります。この場合、2つのレコードが作成されid ます。最初のレコードの属性と対応する値、およびcre_date and cre_timeattribute*colAold and newcolB

これで、監査の解決策はnot very optimized、を作成しましたrow level trigger。これは、そのテーブルの50列ごとに、そのテーブルが(if -else)にchanged基づいているかどうかをチェックし、監査テーブルにデータを入力します。new and old value私は自分の解決策に満足していないので、ここに投稿しています。私が以下のリンクで見た別の解決策:

http://stackoverflow.com/questions/1421645/oracle-excluding-updates-of-one-column-for-firing-a-trigger

私の場合、これは機能していません。以下に示すように、そのためのPOCを実行しました。

create table temp12(id number);

create or replace trigger my_trigger
after update or insert on temp12
for each row
declare
  TYPE tab_col_nt IS table of varchar2(30);

  v_tab_col_nt tab_col_nt;

begin
 v_tab_col_nt := tab_col_nt('id','name');

   for r in v_tab_col_nt.first..v_tab_col_nt.last
   loop
      if updating(r) then
         insert into data_table values(1,'i am updating'||r);
      else
      insert into data_table values(2,'i am inserting'||r);
      end if;
   end loop;

 end;

更新する場合は、else部分を呼び出しています。理由はわかりません。これはcompound trigger

4

5 に答える 5

7

常に呼び出されることに関する当面の問題は、関連する列名を検索するのではなく、elseインデックス変数を直接使用しているためです。r

for r in v_tab_col_nt.first..v_tab_col_nt.last
loop
    if updating(v_tab_col_nt(r)) then
        insert into data_table values(1,'i am updating '||v_tab_col_nt(r));
    else
        insert into data_table values(2,'i am inserting '||v_tab_col_nt(r));
    end if;
end loop;

idまた、テーブルの作成では列のみが表示されているため、r2の場合、常に挿入されていると表示されname、更新されることはありません。さらに重要なことに、name列があり、特定の列に対してのみそれを更新している場合id、このコードは、id変更されていないときに挿入として表示されます。挿入/更新を別々のブロックに分割する必要があります。

if updating then
    for r in v_tab_col_nt.first..v_tab_col_nt.last loop
        if updating(v_tab_col_nt(r)) then
            insert into data_table values(1,'i am updating '||v_tab_col_nt(r));
        end if;
    end loop;
else /* inserting */
    for r in v_tab_col_nt.first..v_tab_col_nt.last loop
        insert into data_table values(2,'i am inserting '||v_tab_col_nt(r));
    end loop;
end if;

nameこれは、列が存在しなくても挿入していることを示しますが、それは間違いだと思いuser_tab_columnsます。本当に動的にしたい場合は、とにかく名前のリストにデータを入力しようとしていると思います。


私は、個々の列ではなく、行全体のコピーを取得する監査テーブルを使用したほうがよいと思われる他のユーザー(少なくとも一部)に同意します。あなたの異議は、どの列が変更されたかを個別にリストすることの複雑さのようです。列ごとのデータが必要なときに監査テーブルのピボットを解除することで、少しの作業でこの情報を取得できます。例えば:

create table temp12(id number, col1 number, col2 number, col3 number);
create table temp12_audit(id number, col1 number, col2 number, col3 number,
    action char(1), when timestamp);

create or replace trigger temp12_trig
before update or insert on temp12
for each row
declare
    l_action char(1);
begin
    if inserting then
        l_action := 'I';
    else
        l_action := 'U';
    end if;

    insert into temp12_audit(id, col1, col2, col3, action, when)
    values (:new.id, :new.col1, :new.col2, :new.col3, l_action, systimestamp);
end;
/

insert into temp12(id, col1, col2, col3) values (123, 1, 2, 3);
insert into temp12(id, col1, col2, col3) values (456, 4, 5, 6);
update temp12 set col1 = 9, col2 = 8 where id = 123;
update temp12 set col1 = 7, col3 = 9 where id = 456;
update temp12 set col3 = 7 where id = 123;

select * from temp12_audit order by when;

        ID       COL1       COL2       COL3 A WHEN
---------- ---------- ---------- ---------- - -------------------------
       123          1          2          3 I 29/06/2012 15:07:47.349
       456          4          5          6 I 29/06/2012 15:07:47.357
       123          9          8          3 U 29/06/2012 15:07:47.366
       456          7          5          9 U 29/06/2012 15:07:47.369
       123          9          8          7 U 29/06/2012 15:07:47.371

したがって、実行されたアクションごとに1つの監査行、2つの挿入、および3つの更新があります。ただし、変更された列ごとに個別のデータを表示する必要があります。

select distinct id, when,
    case
        when action = 'I' then 'Record inserted'
        when prev_value is null and value is not null
            then col || ' set to ' || value
        when prev_value is not null and value is null
            then col || ' set to null'
        else col || ' changed from ' || prev_value || ' to ' || value
    end as change
from (
    select *
    from (
        select id,
            col1, lag(col1) over (partition by id order by when) as prev_col1,
            col2, lag(col2) over (partition by id order by when) as prev_col2,
            col3, lag(col3) over (partition by id order by when) as prev_col3,
            action, when
        from temp12_audit
    )
    unpivot ((value, prev_value) for col in (
        (col1, prev_col1) as 'col1',
        (col2, prev_col2) as 'col2',
        (col3, prev_col3) as 'col3')
    )
)
where value != prev_value
    or (value is null and prev_value is not null)
    or (value is not null and prev_value is null)
order by when, id;

        ID WHEN                      CHANGE
---------- ------------------------- -------------------------
       123 29/06/2012 15:07:47.349   Record inserted
       456 29/06/2012 15:07:47.357   Record inserted
       123 29/06/2012 15:07:47.366   col1 changed from 1 to 9
       123 29/06/2012 15:07:47.366   col2 changed from 2 to 8
       456 29/06/2012 15:07:47.369   col1 changed from 4 to 7
       456 29/06/2012 15:07:47.369   col3 changed from 6 to 9
       123 29/06/2012 15:07:47.371   col3 changed from 3 to 7

5つの監査レコードが7つの更新になりました。3つのupdateステートメントは、変更された5つの列を示しています。これを頻繁に使用する場合は、それをビューにすることを検討してください。

それでは、それを少しだけ分解してみましょう。コアはこの内部選択であり、これを使用lag()して、その前の監査レコードから行の前の値を取得しますid

        select id,
            col1, lag(col1) over (partition by id order by when) as prev_col1,
            col2, lag(col2) over (partition by id order by when) as prev_col2,
            col3, lag(col3) over (partition by id order by when) as prev_col3,
            action, when
        from temp12_audit

これにより、すべての監査テーブル列と、unpivot()操作に使用されるラグ列を含む一時ビューが得られます。これは、質問に11gのタグを付けたときに使用できます。

    select *
    from (
        ...
    )
    unpivot ((value, prev_value) for col in (
        (col1, prev_col1) as 'col1',
        (col2, prev_col2) as 'col2',
        (col3, prev_col3) as 'col3')
    )

id, action, when, col, value, prev_valueこれで、列を持つ一時的なビューができました。この場合、列が3つしかないため、監査テーブルの行数は3倍になります。最後に、値が変更された行、つまりwhere value != prev_value(nullを許可)のみを含むように表示する外部選択フィルター。

select
    ...
from (
    ...
)
where value != prev_value
    or (value is null and prev_value is not null)
    or (value is not null and prev_value is null)

私はcase何かを印刷するために使用していますが、もちろん、データを使って好きなことをすることができます。これdistinctが必要なのinsertは、監査テーブルのエントリもピボットされていないビューで3行に変換され、最初のcase句の3つすべてに同じテキストが表示されているためです。

于 2012-06-29T14:37:22.407 に答える
3

作業を楽にして、列のデータが更新されたときに行全体を挿入してみませんか。したがって、メインテーブルの更新(または通常は削除)では、最初に元の行が監査テーブルにコピーされます。したがって、監査テーブルはメインテーブルと同じレイアウトになりますが、次のような追跡フィールドがいくつか追加されます。

create or replace trigger my_tab_tr
before update or delete
on my_tab
referencing new as new and old as old
for each row
declare
  l_type varchar2(3);
begin
  if (updating) then
    l_type = 'UPD';
  else
    l_type = 'DEL';
  end if;

insert into my_tab_audit(
 col1,
 col2,
 audit_type,
 audit_date) 
values (
 :old.col1,
 :old.col2,
 l_type,
 sysdate
);
end;

必要に応じて監査テーブルに列を追加します。これは典型的な例です。

于 2012-06-29T12:50:54.773 に答える
2

フィールドごとの監査が行われるのを確認した唯一の方法は、フィールド:OLDと:NEWの各値を相互にチェックし、適切なレコードを監査テーブルに書き込むことです。トリガーに適切な値を渡すサブルーチンを含めることでこれを半自動化できますが、何らかの方法で、個々のフィールドごとにコードを記述する必要があると思います。他の誰かが私が知らないある種のリフレクティブAPIを使ってこれを行う素晴らしい方法を持っていない限り(そして「私が知らないこと」は毎日より多くのものに適用できる、またはそう思われる:-)。

個々のフィールドを監査するか、行全体(通常は「履歴」テーブルと呼びます)を監査するかは、データの使用方法によって異なります。この場合、個々のフィールドの変更を報告する必要がある場合は、フィールドごとの監査の方が適しているように思われることに同意します。その他の場合(たとえば、データ抽出を任意の日付で再現可能にする必要がある場合)、行ごとの監査または「履歴テーブル」アプローチの方が適しています。

監査レベル(フィールドごとまたは行ごと)に関係なく、NULL / NOT NULLの場合を処理するように比較ロジックを慎重に作成する必要があります。これにより、次:OLD.FIELD1 = :NEW.FIELD1のいずれかを比較して噛まれないようになります。値(または両方)がNULLであり、NULLはそれ自体でさえも何にも等しくないため、適切なアクションを実行しなくなります。私がどのように知っているか私に聞かないでください...:-)

好奇心から、INSERTが発生したときに作成される単一の行のOLD_VALUEとNEW_VALUEには何が入力されますか?

共有してお楽しみください。

于 2012-06-29T13:55:59.897 に答える
1

私がそれをするのが好きな方法:

  1. 既存の元のテーブルと並行する監査テーブルを作成します。
  2. この監査テーブルにタイムスタンプとユーザー列を追加します。
  3. 元のテーブルが挿入または更新されるたびに、監査テーブルに挿入するだけです。
  4. audiテーブルには、タイムスタンプとユーザー値を設定するトリガーが必要です。他のすべての値は新しい値として入力されます。

そうすれば、誰がいつ何をしたかをいつでも照会できます。

于 2012-06-29T12:44:44.383 に答える
1

非常に非正統的な解決策:(システムテーブルにアクセスできる場合のみ、少なくともSELECT特権)

  1. あなたはあなたのテーブルの名前を知っています。テーブルの所有者のIDを特定します。SYS.USER $で、ユーザー(=所有者)の名前で検索します。

  2. SYS.OBJ $内のテーブルのオブジェクトID(= OBJ#)をOWNER#(=所有者のID)とNAME(=テーブルの名前)で検索します。

  3. SYS.COL $のテーブルを構成する列をOBJ#で検索します。すべての列、それらのID(COL#)、および名前(NAME)が表示されます。

  4. それらの列のセット上を移動するカーソルを使用してUPDATEトリガーを記述します。ループのニュークリアスを1回だけ書き込む必要があります。

  5. 詳細はOracleバージョンごとに異なる可能性があるため、コードは提供しません。

これは本当の動的SQLプログラミングです。私はたまたまかなり大規模なエンタープライズシステム(チームリーダーはそれについて知らなかった)でもそれを使用し、それは機能しました。高速で信頼性があります。欠点:{特権; 可搬性; 責任者からの悪い配慮}。

于 2013-01-03T13:04:02.307 に答える