最近在做一个数据库同步的项目,数据库都是sql,编程语言是c#。
如果是两个实体服务器之间的数据库同步,利用sql的发布订阅就可实现。但是现在这个是要同步服务器和微软azure上的数据库,发布订阅是不行了,只有手动编写数据库同步程序。
而且,我们的数据库比较复杂,各个表之间都有关联,而且ID都是自动增长类型的。查阅了很多资料,都不可行,最后终于找到了一篇文献资料,然后完美的解决了我的问题。
1)、首先,不要循环把每条数据更新到数据库。这种方法不很现实,哪怕数据量非常小,而且还要保证网络连接非常的好。即使满足这些,长时间运行还是会出现连接的timeout超时,等等...
2)、所以,最好用批量导入更新数据的方法,就是SqlDataAdapter。如果单纯的是导入数据,也可以用bulk了,网上代码很多,但是如果有导入和更新那就最好用SqlDataAdapter了。
下面上代码,
1、首先把查询的数据库记录保存下来,查询语句类似于以下语句:
SqlConnection conn = new SqlConnection(ConnectionString);//ConnectionString是数据库连接语句字符串,包含服务器名,数据库名,用户名,密码等
string Querystr=“select * from 表名”;//查询语句字符串。
(复杂一点的语句,灵活运用sql查询语句,现在sql运行一下,没问题就放心写入c#里面。
Querystr = "select * from 表A join 表B on 表A.EfficiencyID=表B.EfficiencyID where left(表A.ShiftCode,8)>CONVERT(varchar(100), GETDATE()-" + day + ", 112)";)
SqlDataAdapter da=new SqlDataAdapter(Querystr, conn);
DataSet ds=new DataSet();
da.Fill(ds);//填充数据集
ids.Add(str, ds);
2、获得了查询结果,程序里怎么摆布都行,我的是从ds里面辨别出哪条语句是插入的insert,哪条语句是更新的update。
插入代码如下:
DataSet dss = new DataSet();
adapter = new SqlDataAdapter(cmd);
commandBulider = new SqlCommandBuilder(adapter);
commandBulider.ConflictOption = ConflictOption.OverwriteChanges;
adapter.SelectCommand = new SqlCommand("select ShiftCode,StartTime,EndTime,StartPlanDownTime,EndPlanDownTime,Overtime,IsClosed from Shift", conn);
adapter.InsertCommand = new SqlCommand("insert into Shift (ShiftCode,StartTime,EndTime,StartPlanDownTime,EndPlanDownTime,Overtime,IsClosed) values (RTRIM(@ShiftCode),@StartTime,@EndTime,@StartPlanDownTime,@EndPlanDownTime,@Overtime,@IsClosed)", conn);
adapter.InsertCommand.Parameters.Add("@ShiftCode", SqlDbType.Char, 10, "ShiftCode");
adapter.InsertCommand.Parameters.Add("@StartTime", SqlDbType.DateTime, 8, "StartTime");
adapter.InsertCommand.Parameters.Add("@EndTime", SqlDbType.DateTime, 8, "EndTime");
adapter.InsertCommand.Parameters.Add("@StartPlanDownTime", SqlDbType.DateTime, 8, "StartPlanDownTime");
adapter.InsertCommand.Parameters.Add("@EndPlanDownTime", SqlDbType.DateTime, 8, "EndPlanDownTime");
adapter.InsertCommand.Parameters.Add("@Overtime", SqlDbType.Decimal, 5, "Overtime");
adapter.InsertCommand.Parameters.Add("@IsClosed", SqlDbType.Bit, 1, "IsClosed");
adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.None;
adapter.UpdateBatchSize = 0;
adapter.Fill(dss);
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
object[] row = {ds.Tables[0].Rows[i].ItemArray.GetValue(1).ToString().Trim(),
ds.Tables[0].Rows[i].ItemArray.GetValue(2).ToString().Trim(),
ds.Tables[0].Rows[i].ItemArray.GetValue(3).ToString().Trim(),
ds.Tables[0].Rows[i].ItemArray.GetValue(5).ToString().Trim(),
ds.Tables[0].Rows[i].ItemArray.GetValue(6).ToString().Trim(),
ds.Tables[0].Rows[i].ItemArray.GetValue(8).ToString().Trim(),
ds.Tables[0].Rows[i].ItemArray.GetValue(9).ToString().Trim()};
dss.Tables[0].Rows.Add(row);
}
try
{
int nn = adapter.Update(dss.Tables[0]);//返回影响的行数
if (nn != ds.Tables[0].Rows.Count)
{
Console.WriteLine("-------------" + tableName + "插入不成功--------------");
}
}
catch (Exception e)
{
if (sw != null)
{
sw.WriteLine(tableName + "插入数据到数据库出错:" + e.ToString() );
}
return false;
}
finally
{
dss.Dispose();
adapter.Dispose();
commandBulider.Dispose();
}
更新代码如下:
DataSet dss = new DataSet();
adapter = new SqlDataAdapter(cmd);
commandBulider = new SqlCommandBuilder(adapter);
commandBulider.ConflictOption = ConflictOption.OverwriteChanges;
adapter.SelectCommand = new SqlCommand("select top "+ds.Tables[0].Rows.Count.ToString()+" ShiftCode,StartTime,EndTime,StartPlanDownTime,EndPlanDownTime,Overtime,IsClosed from Shift", conn);
adapter.Fill(dss);
adapter.UpdateCommand = new SqlCommand("update Shift set StartTime=@StartTime,EndTime=@EndTime,StartPlanDownTime=@StartPlanDownTime,EndPlanDownTime=@EndPlanDownTime,Overtime=@Overtime,IsClosed=@IsClosed where RTRIM(ShiftCode)=RTRIM(@ShiftCode)", conn);
adapter.UpdateCommand.Parameters.Add("@ShiftCode", SqlDbType.Char, 10, "ShiftCode");
adapter.UpdateCommand.Parameters.Add("@StartTime", SqlDbType.DateTime, 8, "StartTime");
adapter.UpdateCommand.Parameters.Add("@EndTime", SqlDbType.DateTime, 8, "EndTime");
adapter.UpdateCommand.Parameters.Add("@StartPlanDownTime", SqlDbType.DateTime, 8, "StartPlanDownTime");
adapter.UpdateCommand.Parameters.Add("@EndPlanDownTime", SqlDbType.DateTime, 8, "EndPlanDownTime");
adapter.UpdateCommand.Parameters.Add("@Overtime", SqlDbType.Decimal, 5, "Overtime");
adapter.UpdateCommand.Parameters.Add("@IsClosed", SqlDbType.Bit, 1, "IsClosed");
adapter.UpdateCommand.UpdatedRowSource = UpdateRowSource.None;
adapter.UpdateBatchSize = 0;
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
dss.Tables[0].Rows[i].BeginEdit();
dss.Tables[0].Rows[i]["ShiftCode"] = ds.Tables[0].Rows[i]["ShiftCode"].ToString().Trim();
dss.Tables[0].Rows[i]["StartTime"] = ds.Tables[0].Rows[i]["StartTime"];
dss.Tables[0].Rows[i]["EndTime"] = ds.Tables[0].Rows[i]["EndTime"];
dss.Tables[0].Rows[i]["StartPlanDownTime"] = ds.Tables[0].Rows[i]["StartPlanDownTime"];
dss.Tables[0].Rows[i]["EndPlanDownTime"] = ds.Tables[0].Rows[i]["EndPlanDownTime"];
dss.Tables[0].Rows[i]["Overtime"] = ds.Tables[0].Rows[i]["Overtime"];
dss.Tables[0].Rows[i]["IsClosed"] = ds.Tables[0].Rows[i]["IsClosed"];
dss.Tables[0].Rows[i].EndEdit();
}
try
{
int nn = adapter.Update(dss.Tables[0]);//返回影响的行数
if (nn != ds.Tables[0].Rows.Count)
{
Console.WriteLine("-------------" + tableName + "插入不成功--------------");
}
}
catch (Exception e)
{
if (sw != null)
{
sw.WriteLine(tableName + "插入数据到数据库出错:" + e.ToString() );
}
return false;
}
finally
{
dss.Dispose();
adapter.Dispose();
commandBulider.Dispose();
}
有一种sql的CLR的类型,c#识别不了,就是hierarchyid类型,它是sql里的树形结构类型,在c#里面可以用SqlDbType.Char来处理,插入数据库的时候用CAST(@SiteNode as HierarchyId)语句就可以了,类似于:
adapter.InsertCommand = new SqlCommand("insert into SiteNode (SiteCode,SiteNode,SiteGroup,IsLeaf,IsParentInput,IsParentOutput,TargetRunRate,TargetCycleTime,MeasuredCycleTime) "
+ "values (RTRIM(@SiteCode),CAST(@SiteNode as HierarchyId),RTRIM(@SiteGroup),@IsLeaf,@IsParentInput,@IsParentOutput,@TargetRunRate,@TargetCycleTime,@MeasuredCycleTime)", conn);
adapter.InsertCommand.Parameters.Add("@SiteNode", SqlDbType.Char, 20, "SiteNode");//hierarchyid
但是update的时候用类似的方法就不行了,我目前还没找到解决方法,用的另外的方法解决的。就是上面提到的一条一条的插入,因为只有这一个表,基本不怎么变化,所以只好采用这种方法了。代码如下:
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
string strUpdate = "update SiteNode set SiteNode='" + ds.Tables[0].Rows[i].ItemArray.GetValue(2).ToString().Trim()
+ "',SiteGroup='" + ds.Tables[0].Rows[i].ItemArray.GetValue(3).ToString().Trim()
+ "',IsLeaf='" + ds.Tables[0].Rows[i].ItemArray.GetValue(4).ToString().Trim()
+ "',IsParentInput='" + ds.Tables[0].Rows[i].ItemArray.GetValue(5).ToString().Trim()
+ "',IsParentOutput='" + ds.Tables[0].Rows[i].ItemArray.GetValue(6).ToString().Trim()
+ "',TargetRunRate=" + ds.Tables[0].Rows[i].ItemArray.GetValue(7).ToString().Trim()
+ ",TargetCycleTime=" + ds.Tables[0].Rows[i].ItemArray.GetValue(8).ToString().Trim()
+ ",MeasuredCycleTime=" + ds.Tables[0].Rows[i].ItemArray.GetValue(9).ToString().Trim()
+ " where SiteCode='" + ds.Tables[0].Rows[i].ItemArray.GetValue(1).ToString().Trim() + "'";
SqlCommand mycommand = new SqlCommand(strUpdate, conn);
mycommand.ExecuteNonQuery();
mycommand.Dispose();
}
目前,数据库同步就做好了,测试了一下,同步一次的时间大概在30S左右。之前的本方法一条一条处理到数据库,时间大概4min左右,同步效率提高了很多。后续有待进一步提高效率,看看还有没有其他的方法。
最后提一下参考的文献资料,对我非常有帮助http://bbs.csdn.net/topics/370090507