4

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

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

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

我在诸如此类的文章中阅读了有关 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

4 回答 4

2

“我存储实际 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
    

由于第一个事务中所做的更改对第二个事务不可见,因此第二个事务更改的行将不包括第一个事务的行。但是如果您设法捕获 DML 并按顺序重放它,事务 1 的更改将是可见的,因此事务 2 的更改将有所不同(请参阅Tom Kyte 的专家 Oracle 数据库架构第二版的第 40 页和第 41 页。)

  1. 希望您使用的是绑定变量,因此 DML 本身没有意义:update table_a set col_a = :col_a where id = :id现在呢?好的,所以你想要带有变量绑定的 DML。

  2. 你使用序列吗?如果是这样,next_val 将不会在 DB1 和 DB2 之间保持同步。(例如,实例失败会导致值丢失,两个系统会同时失败吗?)如果您正在处理 RAC,其中 next_val 因节点而异,请忘记它。

我将从调查 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 回答