用Trigger为MView手动增加时间戳
用Trigger为MView手动增加时间戳
很多人都遇到过这样的问题:源数据库里的表没有时间戳,或者有时间戳的字段但没有严格执行,而历史数据又可能会经常性得被修改。这对于ETL数据抽取来说将是一个很大的问题,特别是对于不能动源库的情况下,很难解决这个问题。最近又因为这个问题,考虑了很久,最后打算在MView同步数据的基础上,通过Trigger来手动增加时间戳。
考虑对MView操作的原因是因为现有的数据同步就是用MView做的,据说用Stream同步数据也可以用类似的办法添加时间戳的,这个相关的问题以后再研究,讲一下MView下的操作。
首先MView有一些天然的限制,因为最初我考虑的是在建MView的时候就添加一个SYSDATE的字段,这样每次同步就会自动将同步时的sysdate也加入到新的物化视图中,但是这个尝试失败了,因为增量(Fast)更新不支持复杂表结构,而Oracle认为新增字段的表都属于复杂表,会报错如下:
ORA-12015: cannot create a fast refresh materialized view from a complex query
然后又希望能够通过物化视图的on commit刷新模式对表进行刷新,这样可以直接查询ora_rowscn来判断某行的更新时间,但是很遗憾,on commit的刷新模式只针对同库下的更新,不支持远程同步,所以没办法只能用Trigger来自己手工增加时间戳。具体的实验步骤如下:
1、在源库中建立表t1,并添加mv log:
SQL> create table t1(a int primary key,b int);
Table created.
SQL> insert into t1 values(1,2);
1 row created.
SQL> insert into t1 values(3,4);
1 row created.
SQL> commit;
Commit complete.
SQL> select * from t1;
A B
---------- ----------
1 2
3 4
---------- ----------
1 2
3 4
SQL> create materialized view log on t1 with primary key;
Materialized view log created.
2、在新 库上添加源库的DB LINK:
SQL> create database link wxq_db
2 connect to wangxiaoqi identified by wangxiaoqi
3 using '(DESCRIPTION =
4 (ADDRESS_LIST =
5 (ADDRESS = (PROTOCOL = TCP)(HOST = 10.20.1.127)(PORT = 1521))
6 )
7 (CONNECT_DATA =
8 (SERVICE_NAME = devWXQowb)
9 )
10 )';
2 connect to wangxiaoqi identified by wangxiaoqi
3 using '(DESCRIPTION =
4 (ADDRESS_LIST =
5 (ADDRESS = (PROTOCOL = TCP)(HOST = 10.20.1.127)(PORT = 1521))
6 )
7 (CONNECT_DATA =
8 (SERVICE_NAME = devWXQowb)
9 )
10 )';
Database link created.
SQL> select * from t1@
wxq_db ;
A B
-------------- --------------
1 2
3 4
A B
-------------- --------------
1 2
3 4
3、在新库创建物化视图,设置增量刷新
SQL> create materialized view wxq_t1
2 build deferred
3 refresh fast
4 on demand
5 with primary key
6 as
7 select * from t1@wxq_db ;
2 build deferred
3 refresh fast
4 on demand
5 with primary key
6 as
7 select * from t1@wxq_db ;
Materialized view created.
SQL> select * from wxq_t1;
no rows selected
--第一次必须要全量刷新
SQL> exec dbms_mview.refresh('wxq_t1','complete');
PL/SQL procedure successfully completed.
SQL> select * from wxq_t1;
A B
---------- ----------
1 2
3 4
---------- ----------
1 2
3 4
4、在新库创建关于表T1的Trigger:
SQL> create table t1_log(a int,c_flag varchar2(2),modifydate date);
Table created.
SQL> create or replace trigger t1_tgr after update or insert or delete on wxq_t1
2 referencing old as oldrow new as newrow for each row
3 begin
4 if inserting then
5 insert into t1_log values(:newrow.a,'I',sysdate);
6 end if;
7 if updating then
8 insert into t1_log values(:oldrow.a,'U',sysdate);
9 end if;
10 if deleting then
11 insert into t1_log values(:oldrow.a,'D',sysdate);
12 end if;
13 end;
14 /
2 referencing old as oldrow new as newrow for each row
3 begin
4 if inserting then
5 insert into t1_log values(:newrow.a,'I',sysdate);
6 end if;
7 if updating then
8 insert into t1_log values(:oldrow.a,'U',sysdate);
9 end if;
10 if deleting then
11 insert into t1_log values(:oldrow.a,'D',sysdate);
12 end if;
13 end;
14 /
Trigger created.
5、来测试一下在源库中进行插入、删除、修改操作
SQL> select * from t1;
A B
---------- ----------
1 2
3 4
---------- ----------
1 2
3 4
SQL> insert into t1 values(10,20);
1 row created.
SQL> update t1 set b=40 where a=3;
1 row updated.
SQL> delete from t1 where a=1;
1 row deleted.
SQL> commit;
Commit complete.
SQL> select * from t1;
A B
---------- ----------
10 20
3 40
---------- ----------
10 20
3 40
SQL> column change_vector$$ format a20
SQL> select * from mlog$_t1;
SQL> select * from mlog$_t1;
A SNAPTIME$$ DM OL CHANGE_VECTOR$$
---------- ---------- -- -- --------------------
10 4000-01-01 I N FE
3 4000-01-01 U U 04
1 4000-01-01 D O 00
---------- ---------- -- -- --------------------
10 4000-01-01 I N FE
3 4000-01-01 U U 04
1 4000-01-01 D O 00
--然后在新库中查看时候生效
SQL> select * from t1_log;
no rows selected
SQL> exec dbms_mview.refresh('wxq_t1','fast');
PL/SQL procedure successfully completed.
SQL> select * from t1_log;
A C_ MODIFYDATE
---------- -- ----------
1 D 2009-06-18
10 I 2009-06-18
3 U 2009-06-18
---------- -- ----------
1 D 2009-06-18
10 I 2009-06-18
3 U 2009-06-18
6、创建最终视图,方便实际操作:
SQL> create or replace view t1 as
2 select wxq_t1.a,wxq_t1.b,t1_log.c_flag,t1_log.modifydate
3 from wxq_t1,t1_log
4 where wxq_t1.a = t1_log.a
5 and t1_log.c_flag in ('I','U')
6 union all
7 select t1_log.a,wxq_t1.b,t1_log.c_flag,t1_log.modifydate
8 from wxq_t1,t1_log
9 where t1_log.a = wxq_t1.a(+)
10 and t1_log.c_flag = 'D';
2 select wxq_t1.a,wxq_t1.b,t1_log.c_flag,t1_log.modifydate
3 from wxq_t1,t1_log
4 where wxq_t1.a = t1_log.a
5 and t1_log.c_flag in ('I','U')
6 union all
7 select t1_log.a,wxq_t1.b,t1_log.c_flag,t1_log.modifydate
8 from wxq_t1,t1_log
9 where t1_log.a = wxq_t1.a(+)
10 and t1_log.c_flag = 'D';
View created.
SQL> select * from t1;
A B C_ MODIFYDATE
---------- ---------- -- ----------
10 20 I 2009-06-18
3 40 U 2009-06-18
1 D 2009-06-18
---------- ---------- -- ----------
10 20 I 2009-06-18
3 40 U 2009-06-18
1 D 2009-06-18
这样就创建完成了,不但为新记录添加了时间戳,而且也增加了被删除记录的时间(被删除记录只剩下主键和时间字段信息)。不过这个方法有些过于繁琐,只能在对少数表加时间戳的情况下使用,而不能全库操作,而且针对每一个表都需要建一个trigger,效率也是个问题。但是好处是创建之后对于前台查询时完全透明的,注意最终的查询view名称是与源库的表名一致的,这样的封装性对前台操作的感觉非常好。