oracle数据库表部分字段双向同步,利用包和触发器实现
需求:数据库里面有两张表,结构不完全相同,需要实时同步两张表的部分字段
实现方案:两张表上分别建立触发器进行同步,但是这样操作会有个问题,就会导致触发器循环触发。那么就需要在触发器进行触发前,进行一个判断,如果操作是由触发器引发的,那么就不做操作,反之,执行触发器定义的操作。想要实现这个,我开始考虑过在触发器a里面暂时使触发器b失效,但是发现alter trigger disable不合法;然后看到有人提到在表里面新增一个字段,用来做标识,但是这样需要修改表结构,虽然可以达到要求,但是总觉得不是很好,所以也没有使用;后来通过学习oracle对象,了解到了包的特性,所以决定采用包的方式来实现,利用包的变量来做标识。
/*操作过程中,需要分别执行‘包’,‘函数’,‘触发器’;一次性执行会有问题 操作完成后,注意检查‘包’,‘函数’,‘触发器’状态是否有效*/ --创建包头 create or replace package pk_check_active is --标识是否为触发器引发 n number :=0;
--获取是否我触发器触发标识,1为触发器触发 function getactive return number;
--设置状态 procedure setactive(n1 in number); end pk_check_active;
/*这里的/一定不能缺少*/ / create or replace package body pk_check_active as function getactive return number is begin return n; end getactive; procedure setactive(n1 in number) is begin n := n1; end setactive; end pk_check_active; --获取guid的方式,采用了8-4-4-4-12的格式 create or replace function Creategs_oid return varchar2 is guid varchar(64); result varchar(64); begin guid := sys_guid(); result := substr(guid,1,8)||'-'||substr(guid,9,4)||'-'|| substr(guid,13,4)||'-'||substr(guid,17,4)||'-'||substr(guid,21,12); return (result); end Creategs_oid; --触发表tableA create or replace trigger tr_cs_user after insert or update or delete on tableA for each row begin if pk_check_active.getactive() =1 then pk_check_active.setactive(2); return; else pk_check_active.setactive(1); end if; if inserting then insert into tableB (gs_oid,s_username,s_password) values (Creategs_oid(),:new.name,:new.pass); elsif updating then update taableB t set t.s_username=:new.name ,t.s_password=:new.pass where t.s_username=:old.name; elsif deleting then delete from tableB t where t.s_username = :old.name; end if; pk_check_active.setactive(0); end; --获取指定列最大值+1(也是一种id的标识方法,开始没有做成自增字段,所以写触发器的时候需要自己来实现了) create or replace function GetCSUserID return number is result number; begin select max(userid)+1 into result from tableA; return (result); end GetCSUserID; --触发tableB create or replace trigger tr_bs_user after insert or update or delete on tableB for each row begin if pk_check_active.getactive() =1 then pk_check_active.setactive(2); return; else pk_check_active.setactive(1); end if; if inserting then insert into tableA (userid,name,pass,version,useable,remark) values (GetCSUserID(),:new.s_username,:new.s_password,'SDE.DEFAULT','0','用户自动添加'); elsif updating then update tableA t set t.name=:new.s_username, t.pass=:new.s_password where t.name=:old.s_username; elsif deleting then delete from tableA t where t.name = :old.s_username; end if; pk_check_active.setactive(0); end;
上面的代码,涉及到了包,触发器,函数,存储过程。通过这个小事例,可以了解到常用触发器的用法,同时还有格式化guid的方法,应该对大家还是有一些用处的。这种方式,只能对同一个用户下的表进行同步,如果是跨用户的情况下,这种方式是无效的。
引用"由于package的全局变量在数据库层次上并不可见,所以每个session都可以认为是一个被实例化了的package对象。在session级别上对全局变量执行的赋值操作并不会被其他session看到,很好地体现了数据的封装性。"所以说,通过包的变量来控制,必须是在一个session下。按照我的理解,如果采用ado方式,那么一个连接就算是一个session。那么有没有跨session的访问的方式呢,问了一下谷歌,是有解决方式,不过我测试了没成功,就不卖弄了,在参考文章里面会提到。
参考内容:http://www.2cto.com/database/201203/123813.html
http://www.cnblogs.com/huyong/archive/2011/04/27/2030466.html
http://blog.chinaunix.net/uid-186431-id-84823.html
http://www.cnblogs.com/tukzer/archive/2010/12/08/1900047.html