項目環境:公司中心有一個數據庫,產品部署地有多個,每個部署地是一個局域網,部署地內有一臺對外通訊的服務器(可能會斷網)。項目需要將每個部署地的部分數據,同步到中心數據庫。
基本思路:在需要同步的數據庫的說明中添加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; }
}
}