不同数据库不同表结构的oracle数据库定时更新方法
问题描述:数据库B需要数据库A中的某些详细信息,且需要定时更新数据库B的数据,最好能做到实时更新
整体方法描述:通过webService来实现。首先是CLIENT端发送请求到SERVICE端,收到请求后将需要的数据全部打包发送给CLIENT端,CLIENT端收到数据
后插入或更新目标数据库的临时表中,临时表通过触发器来更新或插入到表中,同时更新源数据库中临时表的时间字段信息。
具体方法如下:
1、首先在数据库A中通过建立视图来查找出数据库B需要的字段信息。
此时需注意字段结构要一样。
2、在源数据库A中建立一张临时表
create table t_temp(
t_temp_id number(18) primary key not null,
updatetime datetime,
tablename varchar2(20),
status char(1)
);
3、service 端传送数据方法,为确保数据的安全性,在传送数据的过程中需要对数据进行压缩传送方法
数据打包压缩成一个DataSet后发送给CLIENT 端
数据压缩与解压缩方法:
public class ZipUnZip { public static byte[] ZipData(DataSet ds) { byte[] zipData = null; //DataSet ds = LoadUserInfo(q).Copy(); ds.RemotingFormat = SerializationFormat.Binary; BinaryFormatter ser = new BinaryFormatter(); MemoryStream unMS = new MemoryStream(); ser.Serialize(unMS, ds); byte[] bytes = unMS.ToArray(); int lenbyte = bytes.Length; MemoryStream compMs = new MemoryStream(); GZipStream compStream = new GZipStream(compMs, CompressionMode.Compress, true); compStream.Write(bytes, 0, lenbyte); compStream.Close(); unMS.Close(); compMs.Close(); zipData = compMs.ToArray(); return zipData; } public static DataSet UnzipData(byte[] da) { DataSet ds = new DataSet(); try { //WS.Service1 wss = new WSZipDemo.WS.Service1();//WebReference //byte[] da = wss.getZipData(); MemoryStream input = new MemoryStream(); input.Write(da, 0, da.Length); input.Position = 0; GZipStream gzip = new GZipStream(input, CompressionMode.Decompress, true); MemoryStream output = new MemoryStream(); byte[] buff = new byte[4096]; int read = -1; read = gzip.Read(buff, 0, buff.Length); while (read > 0) { output.Write(buff, 0, read); read = gzip.Read(buff, 0, buff.Length); } gzip.Close(); byte[] rebytes = output.ToArray(); output.Close(); input.Close(); MemoryStream ms = new MemoryStream(rebytes); BinaryFormatter bf = new BinaryFormatter(); object obj = bf.Deserialize(ms); ds = (DataSet)obj; } catch (Exception ex) { //MessageBox.Show(ex.Message); } return ds; } }
4、CLIENT端收到数据后,对目标数据库中的临时表进行操作(临时表的结构与正式表结构一样),将数据全部插入到临时表中,
插入数据到临时表中时, 最好先清空临时表中的信息。(truncate table table_name;)
ds是SERVICE端传送过来的DataSet数据集.
CLIENT 端可调用方法:Boolean b = oracleHelper.GetSqlBulkCopy(ds, oracleConn);
public class OracleHelper { /// <summary> /// 大批量入库处理 /// </summary> /// <param name="dtData"></param> /// <returns></returns> public Boolean GetSqlBulkCopy(DataSet dsData, OracleConnection conn) { Boolean bolReturn = false; foreach (DataTable dtData in dsData.Tables) { bolReturn = GetSqlBulkCopyByTable(dtData, conn); } return bolReturn; } public Boolean GetSqlBulkCopyByTable(DataTable dtData, OracleConnection oracleConn) { int BulkCopySize = 500; int BulkCopyTimeOut = 1000 * 60 * 60 * 60; Boolean bolReturn = false; OracleBulkCopy bulkCopy = null; try { bulkCopy = new OracleBulkCopy(oracleConn); bulkCopy.DestinationTableName = dtData.TableName + "_TEMP"; bulkCopy.BatchSize = BulkCopySize; bulkCopy.BulkCopyOptions = OracleBulkCopyOptions.UseInternalTransaction; bulkCopy.BulkCopyTimeout = BulkCopyTimeOut; bulkCopy.WriteToServer(dtData); bolReturn = true; } catch { bolReturn = false; throw; } finally { bulkCopy = null; } return bolReturn; } }
5、通过临时表触发器来更新或插入目标数据库B中的数据
触发器格式案例如下:
CREATE OR REPLACE TRIGGER T_TEMP_trg AFTER INSERT OR UPDATE ON T_TEMP FOR EACH ROW DECLARE PRAGMA AUTONOMOUS_TRANSACTION; resultCount number :=0; maxID number :=0; BEGIN -- 查询是否有记录 以此判断是否更新插入 SELECT count(*) INTO resultCount FROM T_TDJY WHERE YWLSH = :NEW.YWLSH AND YXTYWLSH = :NEW.YXTYWLSH AND BDBH = :NEW.BDBH AND BJJE =:NEW.BJJE; --下一条记录的ID SELECT max(T_TDJY_ID) INTO maxID FROM T_TDJY; --maxID :=maxID+1; IF(resultCount=0) THEN INSERT INTO T_TDJY VALUES( nvl(maxID,0)+1, :NEW.YWLSH, :NEW.BBH, :NEW.XZQH, :NEW.YXTYWLSH, :NEW.JMRBH, :NEW.BDBH, :NEW.JMRIP, :NEW.BJJE ); ELSE UPDATE T_TDJY SET YWLSH= :NEW.YWLSH, BBH= :NEW.BBH, XZQH= :NEW.XZQH, YXTYWLSH=:NEW.YXTYWLSH, JMRBH= :NEW.JMRBH, BDBH= :NEW.BDBH, JMRIP= :NEW.JMRIP, BJJE= :NEW.BJJE WHERE YWLSH = :NEW.YWLSH AND YXTYWLSH = :NEW.YXTYWLSH AND BDBH =:NEW.BDBH AND BJJE =:NEW.BJJE; END IF; -- DELETE FROM T_TDJY; commit; END;
6、如果不进行一定的处理时,在通过临时表的触发器来进行插入或更新数据目标数据库数据时,会出现 :直接路径不支持使用触发器的错误。
因为OracleBulkCopy类不支持事务处理。
解决方法:
1、将源数据库中的数据插入到目标数据库临时表中时,先将目标数据库中临时表的触发器禁用掉,然后调用下面方法:
Boolean b = oracleHelper.GetSqlBulkCopy(ds, oracleConn);
将数据全部插入到目标数据库中,启用触发器,然后通过更新临时表中的字段(doupdate)来触发。此时需要在源数据库A的视图和目标数据库B
的临时表中增加一个临时的字段(doupdate)。
禁用和启用触发器的存储过程方法如下:
create or replace procedure Trigger_OnOff(p_OnOff number) is cursor c is select TABLE_NAME from user_all_tables WHERE TABLE_NAME LIKE '%_TEMP'; tablename varchar2(100); sqlstr varchar2(1000); begin open c; loop fetch c into tablename; exit when c%notfound; if p_OnOff=0 then --0禁用 1启用 sqlstr:='alter table '||tablename||' disable all triggers'; else sqlstr:='alter table '||tablename||' enable all triggers'; end if; execute immediate sqlstr; -- dbms_output.put_line(sqlstr); end loop; close c; end;
CLIENT端调用存储过程来禁用触发器
OracleCommand OracleCmd2 = new OracleCommand("TRIGGER_ONOFF", oracleConn);
OracleCmd2.CommandType = CommandType.StoredProcedure;
OracleParameter id = OracleCmd2.Parameters.Add("@b", OracleDbType.Int32);
id.Value = 0;
OracleCmd2.ExecuteNonQuery();
//调用存储过程来启用触发器
OracleCommand OracleCmd3 = new OracleCommand("TRIGGER_ONOFF", oracleConn);
OracleCmd3.CommandType = CommandType.StoredProcedure;
OracleParameter id1 = OracleCmd3.Parameters.Add("@b", OracleDbType.Int16);
id1.Value = 1;
OracleCmd3.ExecuteNonQuery();
7、CLIENT端添加定时器
private System.Timers.Timer timer1;
timer1 = new System.Timers.Timer(1000 * 60 * 2);
timer1.Elapsed += new System.Timers.ElapsedEventHandler(Timer1Action);
timer1.AutoReset = true;
timer1.Enabled = true;
public void Timer1Action(object sender, System.Timers.ElapsedEventArgs e)
{
//事务处理方法
}