2010-08-17 62 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; 
    
    */