我可以在Oracle触发器中看到DML吗?

时间:2010-08-17 23:54:34

标签: oracle triggers dml

是否可以看到正在运行的DML(SQL语句)导致触发器被执行?

例如,在INSERT触发器中我想得到这个:

“插入myTable(名称)值('Fred')”

我在this这样的文章中读过ora_sql_txt(sql_text)但是无法让它工作 - 不确定这是否导致我走上了正确的道路?

我们正在使用Oracle 10。

提前谢谢。

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

[已编辑]更多细节:我们需要将现有数据库(DB1)复制到通过网络 可访问的机密数据库(DB2)中。我需要保持这些数据库同步。这是从(DB1)到(DB2)的单向同步,因为(DB2)将包含(DB1)系统中未包含的其他表和数据。

我必须确定一种同步这些数据库的方法,而不是让它们失效(例如,备份和恢复)因为它需要保持活动状态。所以我认为,如果我可以存储正在运行的实际DML(当数据发生变化时),我可以“回放”新数据库上的DML来更新它,就像有人手动输入它一样。

由于它的大小,我无法将所有数据都带过来,因为FK约束和插入/更新记录的顺序,我不能仅仅复制已更改的记录。我想如果我可以“回放”发生的事情的日志,使用更改主服务器的确切SQL,我可以保持数据库同步。

我目前的攻击计划是记录所有已更改,插入和删除的记录,当我想要同步时,系统会生成DML以插入/更新/删除这些记录。然后我将.SQL文件带到分类系统并运行脚本。我遇到的问题是FK。 (因为当我生成DML时,我只知道数据的当前状态是什么,而不是它到达那里的路径 - 所以语句的排序是个问题)。我想我可以禁用所有FK,进行合并,然后重新启用所有FK ......

那么 - 我的存储实际DML的方法是否会吸收池塘水,或者是否有更好的解决方案?

4 个答案:

答案 0 :(得分: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;

确切的列值可用于:old和:new伪列。

答案 1 :(得分: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
    
  3. 由于第一笔交易中的更改对第二笔交易不可见,因此第二笔交易所更改的行将不包括第一笔交易的行。但是,如果您设法捕获DML并按顺序重播,则事务1的更改将是可见的,因此事务2的更改将不同。 (参见Tom Kyte's 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之间的同步。 (例如,实例失败会导致丢失值,两个系统是否会同时失败?)如果您正在处理RAC,其中next_val因节点而异,请忘记它。

    3. 我首先要调查Oracle的replication

答案 2 :(得分:1)

我有一种情况需要在测试后将元数据/配置更改(存储在少数几个表中)从开发环境移动到生产环境。像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;

*/

答案 3 :(得分:0)

该功能仅适用于here所述的'事件'触发器。 您应该将Fine-Grained Auditing视为一种机制。详情here