是否可以看到正在运行的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的方法是否会吸收池塘水,或者是否有更好的解决方案?
答案 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的方法是否会吸收池塘水?”是..
DB1上DML的严格排序并不存在。多个进程,多核,基本上同时发生的事情。
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's Expert Oracle Database Architecture Second Edition的第40和41页。)
希望您使用绑定变量,因此DML本身没有意义:update table_a set col_a = :col_a where id = :id
现在怎样?好的,所以你想要DML与它的变量绑定。
你使用序列吗?如果是这样,next_val将不保持DB1和DB2之间的同步。 (例如,实例失败会导致丢失值,两个系统是否会同时失败?)如果您正在处理RAC,其中next_val因节点而异,请忘记它。
我首先要调查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