需求:資料庫裡面有兩張表,結構不完全相同,需要即時同步兩張表的部分欄位
實現方案:兩張表上分別建立觸發器進行同步,但是這樣操作會有個問題,就會導致觸發器迴圈觸發。那麼就需要在觸發器進行觸發前,進行一個判斷,如果操作是由觸發器引發的,那麼就不做操作,反之,執行觸發器定義的操作。想要實現這個,我開始考慮過在觸發器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;--觸發表tableAcreate or replace trigger tr_cs_user after insert or update or deleteon tableA for each rowbegin 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 numberis result number;begin select max(userid)+1 into result from tableA;return (result);end GetCSUserID;--觸發tableBcreate or replace trigger tr_bs_user after insert or update or deleteon 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的訪問的方式呢,問了一下Google,是有解決方式,不過我測試了沒成功,就不賣弄了,在參考文章裡面會提到。
參考內容: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