內外網數據庫同步方式

項目環境:公司中心有一個數據庫,產品部署地有多個,每個部署地是一個局域網,部署地內有一臺對外通訊的服務器(可能會斷網)。項目需要將每個部署地的部分數據,同步到中心數據庫。

基本思路:在需要同步的數據庫的說明中添加Update標識,在需要同步的表中添加UpdateDate字段。在每一天的0點時候,會執行一次同步,根據UpdateDate字段進行時間比對,同步會遍歷當前數據庫的所以後表,如果表中包含Update標識,就執行同步操作。同步時執行事務操作,全部數據同步完成後進行提交操作。如果有錯誤,則執行RollBack,併發送郵件通知維護人員。


本地數據庫LocalDB

遠端數據庫RemoteDB


表名 TableName

主鍵PrimaryKey


簡要步奏

0. BeginTrans


LocalDB:

1  從LocalDB讀出DateTable


RemoteDB:

2. 數據庫使用select * into tmpTable from TableName where PrimaryKey=-1 生成臨時表

3. 將數據集提交到臨時表中

4. 
---更新相同數據 / 刪除相同數據  二選一即可
 --更新(需要對知道每個表的哪些字段需要更新)
update TableName set CreateDate=A.CreateDate from tmpTable A,TableName B where B.PrimaryKey=A.PrimaryKey

 --刪除

       delete from TableName  where PrimaryKey in (select PrimaryKey from tmpTable)



---插入不同數據的記錄
insert into TableName   select * from tmpTable A where not exists(select PrimaryKey  from TableName   B where B.{1}=A.PrimaryKey )
5. 跳到第一步 直到所有表都更新完成


6. Commit



public class Base_TableUpdate
    {
        public int Run(DateTime lastUpdateDate)
        {
            IDatabase dbRemote = null;
            int updateCount = 0;
            try
            {
                IDatabase dbLocal = DataFactory.Database("LocalSqlServer");
                dbRemote = DataFactory.Database("RemoteSqlServer");

                DbTransaction trans = dbRemote.BeginTrans();


                //查詢出所有表
                /*
                select
                  ROW_NUMBER() OVER (ORDER BY a.object_id) AS No, 
                  a.name AS TableName,
                  isnull(g.[value],'') AS Explain
                from
                  sys.tables a left join sys.extended_properties g
                  on (a.object_id = g.major_id AND g.minor_id = 0)
                */
                string sql = string.Format("select ROW_NUMBER() OVER (ORDER BY a.object_id) AS No, a.name AS TableName, isnull(g.[value],'') AS Explain  from sys.tables a left join sys.extended_properties g on (a.object_id = g.major_id AND g.minor_id = 0)");
                List<TableNameEntity> list = dbLocal.FindListBySql<TableNameEntity>(sql);

                foreach(TableNameEntity en in list)
                {
                    if (string.IsNullOrEmpty(en.Explain) || !en.Explain.ToLower().Contains("update"))
                        continue;

                    string tableName = en.TableName;
                    //1 查出數據
                    //where UpdateDate>'{1}'
                    DataSet ds = dbLocal.FindDataSetBySql(string.Format("select * from {0} where UpdateDate>'{1}'", tableName, lastUpdateDate.ToString("yyyy-MM-dd hh:mm:ss")));

                    updateCount += ds.Tables[0].Rows.Count;

                    //2 創建臨時表
                    int res = dbRemote.FindCountBySql("select Count(Name) from sysobjects where name='tmpTable'");
                    if (res == -1)
                    {//失敗
                        throw new Exception("10001 查詢臨時表時失敗,Code{ dbLocal.FindCountBySql(\"select Count(Name) from sysobjects where name='tmpTable'\"); }");
                    }
                    else if (res == 1)
                    {//表存在,刪除表
                        dbRemote.ExecuteBySql(new StringBuilder("Drop Table tmpTable"));
                    }
                    //創建
                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("select * into tmpTable from {0} where (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME='{0}') is null", tableName);
                    res = dbRemote.ExecuteBySql(sb);
                    if (res == -1)
                    {
                        throw new Exception("10002 創建臨時表時失敗,Code{ sb.AppendFormat(\"select * into tmpTable from {0} where (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME='{0}') is null\", tableName);dbRemote.ExecuteBySql(sb); }");
                    }


                    //3 往臨時表插入數據
                    //獲得表主鍵
                    sb = new StringBuilder();
                    sb.AppendFormat("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME='{0}'", tableName);
                    PrimaryKeyEntity entity = dbRemote.FindEntityBySql<PrimaryKeyEntity>(sb.ToString());

                    string primaryKey = entity.COLUMN_NAME;
                    if (string.IsNullOrEmpty(primaryKey))
                    {
                        string errmsg = string.Format("10006 表主鍵爲空,Code{ sb.AppendFormat(\"SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME='{0}'\", tableName); } Parameter{ tableName:{0}}", tableName);
                        throw new Exception(errmsg);
                    }

                    //插入所有數據
                    ds.Tables[0].TableName = "tmpTable";
                    bool bInRes = dbRemote.BulkInsert(ds.Tables[0], trans);
                    if (!bInRes)
                    {
                        string errmsg = string.Format("10003 插入所有數據時失敗,Code{ ds.Tables[0].TableName = \"tmpTable\";bool bInRes = dbRemote.BulkInsert(ds.Tables[0], trans); }");
                        throw new Exception(errmsg);
                    }


                    //4 合併臨時表與服務器對應表數據
                    //刪除已有數據
                    //delete from ' + @TableName + ' where ' + @PKey + ' in (select ' + @PKey + ' from tmpTable)
                    sb = new StringBuilder();
                    sb.AppendFormat("delete from {0} where {1} in (select {1} from tmpTable)", tableName, primaryKey);
                    res = dbRemote.ExecuteBySql(sb, trans);
                    if (res == -1)
                    {
                        string errmsg = string.Format("10004 刪除已有數據時失敗,Code{ sb.AppendFormat(\"delete from {0} where {1} in (select {1} from tmpTable)\", tableName, primaryKey);res =dbRemote.ExecuteBySql(sb); } Parameter{ tableName:{0}, primaryKey:{1}}", tableName, primaryKey);
                        throw new Exception(errmsg);
                    }

                    //將數據插入表
                    //insert into tmpTable select * from ' + @TableName + ' A where not exists(select ' + @PKey + ' from tmpTable B where B.' + @PKey + '=A.' + @PKey + ')';
                    sb = new StringBuilder();
                    sb.AppendFormat("insert into {0} select * from tmpTable A where not exists(select {1} from {0} B where B.{1}=A.{1})", tableName, primaryKey);
                    res = dbRemote.ExecuteBySql(sb, trans);
                    if (res == -1)
                    {
                        string errmsg = string.Format("10005 合併數據時失敗,Code{ sb.AppendFormat(\"insert into {0} select * from tmpTable A where not exists(select {1} from {0} B where B.{1}=A.{1})\", tableName, primaryKey);res = dbRemote.ExecuteBySql(sb); } Parameter{ tableName:{0}, primaryKey:{1}}", tableName, primaryKey);
                        throw new Exception(errmsg);
                    }

                    //5 刪除臨時表
                    dbRemote.ExecuteBySql(new StringBuilder("Drop Table tmpTable"));
                }
                dbRemote.Commit();

                return updateCount;
            }
            catch(Exception ex)
            {
                try{ dbRemote.Rollback(); } catch { }
                
                throw ex;
            }
        }
    }

    public class PrimaryKeyEntity
    {
        private string _COLUMN_NAME;

        /// <summary>
        /// 表的主鍵名
        /// </summary>
        public string COLUMN_NAME
        {
            get { return _COLUMN_NAME; }
            set { _COLUMN_NAME = value; }
        }
    }

    public class TableNameEntity
    {
        private string _TableName;

        public string TableName
        {
            get { return _TableName; }
            set { _TableName = value; }
        }
        private string _Explain;
        public string Explain
        {
            get { return _Explain; }
            set { _Explain = value; }
        }
    }


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章