使用SqlBulkCopy类结合DataTable类快速完成对目标为SQLSERVER的数据交换
2009-12-04 14:29
701 查看
Microsoft SQL Server 提供一个称为 bcp 的流行的命令提示符实用工具,用于将数据从一个表移动到另一个表(表既可以在同一个服务器上,也可以在不同服务器上)。SqlBulkCopy 类允许编写提供类似功能的托管代码解决方案。还有其他将数据加载到 SQL Server 表的方法(例如 INSERT 语句),但相比之下 SqlBulkCopy 提供明显的性能优势。
使用 SqlBulkCopy 类只能向 SQL Server 表写入数据。但是,数据源不限于 SQL Server;可以使用任何数据源,只要数据可加载到 DataTable 实例或可使用 IDataReader 实例读取数据。
源表分几个业务块,每一块有多张表,其中一张主表,其余若干子表。
主表主键和子表关联外键为字符型,据查是GUID字符串。
目标表业务模块基本一致。
主表主键和子表关联外键为数字型,据查是INT32类型。
源表和目标表一些不一致的字段类型,比如date到varchar(8),varchar2(1)到bit等。
源主到目标主表时,目标主键需要动态生成,同时目标子表的外键也需和主表主键对应。
思路如下:
先取得主表数据,然后生成DataTable类,附加一列动态生成目标主键列(轮询所有行,值从1到该DataTable的行数)。执行主表数据的插入。
依次取得子表所有数据DataTable,每张子表先增加一列,轮询所有行,按源外键关联主表DataTable找到动态生成的目标主键列,赋值给增加的那列。对新生成的子表DataTable依次插入目标子表。
主表的标志,子表的标志,源表和目标表的各个字段的映射等都保存在配置文件中。
代码
private void buttonBegin_Click(object sender, EventArgs e)
{
if (String.IsNullOrEmpty(textBoxFile.Text.Trim()))
{
MessageBox.Show("请选择配置文件!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
try
{
XmlDocument doc = new XmlDocument();
doc.Load(textBoxFile.Text.Trim());
XmlNodeList mainTableNodes = doc.SelectNodes("configuration/TableMaps/MainTable");
DataTable dtSourceMain = new DataTable();
using (OracleConnection conn = new OracleConnection(SourceConnectString))
{
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
StringBuilder sbSql = new StringBuilder();
String srcTableName;
String srcKey;
String destTableName;
String destKey;
if (mainTableNodes.Count < 1)
{
MessageBox.Show("配置文件不对,缺少主表!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
srcTableName = mainTableNodes[0].Attributes["SrcTableName"].Value;
srcKey = mainTableNodes[0].Attributes["SrcKey"].Value;
destTableName = mainTableNodes[0].Attributes["DestTableName"].Value;
DeleteTargetTable(destTableName);
destKey = mainTableNodes[0].Attributes["DestKey"].Value;
sbSql.Append("select ");
foreach (XmlNode node in mainTableNodes[0])
sbSql.AppendFormat("{0}, ", node.Attributes["SqlText"].Value);
//sbSql.Remove(sbSql.ToString().LastIndexOf(","), 1);
sbSql.Append(srcKey);
sbSql.AppendFormat(" from {0}", srcTableName);
cmd.CommandText = sbSql.ToString();
OracleDataAdapter adapter = new OracleDataAdapter(cmd);
adapter.Fill(dtSourceMain);
dtSourceMain.Columns.Add(destKey, Type.GetType("System.Int32"));
for (int i = 0; i < dtSourceMain.Rows.Count; i++)
{
dtSourceMain.Rows[i][destKey] = i + 1;
}
using (SqlBulkCopy sqlBC = new SqlBulkCopy(TargetConnectString, SqlBulkCopyOptions.UseInternalTransaction))
{
sqlBC.BatchSize = 1000;
sqlBC.BulkCopyTimeout = 300;
sqlBC.DestinationTableName = destTableName;
//主表数据插入
sqlBC.ColumnMappings.Add(destKey, destKey);
foreach (XmlNode childNode in mainTableNodes[0].ChildNodes)
{
sqlBC.ColumnMappings.Add(childNode.Attributes["SrcColumn"].Value, childNode.Attributes["DestColumn"].Value);
}
sqlBC.WriteToServer(dtSourceMain);
//准备子表数据
XmlNodeList subTableNodes = doc.SelectNodes("configuration/TableMaps/SubTable");
progressBar.Maximum = subTableNodes.Count;
this.progressBar.Value = 1;
this.progressBar.Step = 1;
this.progressBar.PerformStep();
String srcForeignKey;
String destForeignKey;
foreach (XmlNode node in subTableNodes)
{
this.progressBar.PerformStep();
DataTable dtSourceSub = new DataTable();
srcTableName = node.Attributes["SrcTableName"].Value;
srcForeignKey = node.Attributes["SrcForeignKey"].Value;
destTableName = node.Attributes["DestTableName"].Value;
DeleteTargetTable(destTableName);
destForeignKey = node.Attributes["DestForeignKey"].Value;
sqlBC.DestinationTableName = destTableName;
sqlBC.ColumnMappings.Clear();
sqlBC.ColumnMappings.Add(destForeignKey, destForeignKey);
sbSql = new StringBuilder();
sbSql.Append("select ");
foreach (XmlNode childnodes in node.ChildNodes)
{
sbSql.AppendFormat("{0}, ", childnodes.Attributes["SqlText"].Value);
sqlBC.ColumnMappings.Add(childnodes.Attributes["SrcColumn"].Value, childnodes.Attributes["DestColumn"].Value);
}
sbSql.Append(srcForeignKey);
sbSql.AppendFormat(" from {0}", srcTableName);
cmd.CommandText = sbSql.ToString();
adapter = new OracleDataAdapter(cmd);
adapter.Fill(dtSourceSub);
dtSourceSub.Columns.Add(destForeignKey, Type.GetType("System.Int32"));
for (int i = 0; i < dtSourceSub.Rows.Count; i++)
{
DataRow[] drs = dtSourceMain.Select(String.Format("{0}='{1}'", srcKey, dtSourceSub.Rows[i][srcForeignKey]));
if (drs.Length > 0)
dtSourceSub.Rows[i][destForeignKey] = drs[0][destKey];
}
sqlBC.WriteToServer(dtSourceSub);
}
}
}
MessageBox.Show("数据转换成功,请查看目标表数据", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
progressBar.Value = 0;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
各个数据类型的转换的时候,必须在映射的DataTable已经转换好,最好是查询的SQL语句,已经转换好,相见配置文件。
插入的时候,目标表不能有重复主键,否则失败;每条记录的每个字段的实际值大小不能超过目标表,否则报错。
使用 SqlBulkCopy 类只能向 SQL Server 表写入数据。但是,数据源不限于 SQL Server;可以使用任何数据源,只要数据可加载到 DataTable 实例或可使用 IDataReader 实例读取数据。
需求
需要完成一个多张表的数据交换,源数据库为oracle,目标为SqlServer。源表分几个业务块,每一块有多张表,其中一张主表,其余若干子表。
主表主键和子表关联外键为字符型,据查是GUID字符串。
目标表业务模块基本一致。
主表主键和子表关联外键为数字型,据查是INT32类型。
分析
源和目标表的数据内容基本相似,最大区别为主键不一致,导致源表主键无法放入目标表,目标每条记录的主键必须重新赋值。源表和目标表一些不一致的字段类型,比如date到varchar(8),varchar2(1)到bit等。
解决方法
由于SqlBulkCopy类能够批量插入数据到SqlServer,故采用该类作为数据交换。源主到目标主表时,目标主键需要动态生成,同时目标子表的外键也需和主表主键对应。
思路如下:
先取得主表数据,然后生成DataTable类,附加一列动态生成目标主键列(轮询所有行,值从1到该DataTable的行数)。执行主表数据的插入。
依次取得子表所有数据DataTable,每张子表先增加一列,轮询所有行,按源外键关联主表DataTable找到动态生成的目标主键列,赋值给增加的那列。对新生成的子表DataTable依次插入目标子表。
源码
配置文件:主表的标志,子表的标志,源表和目标表的各个字段的映射等都保存在配置文件中。
代码
private void buttonBegin_Click(object sender, EventArgs e)
{
if (String.IsNullOrEmpty(textBoxFile.Text.Trim()))
{
MessageBox.Show("请选择配置文件!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
try
{
XmlDocument doc = new XmlDocument();
doc.Load(textBoxFile.Text.Trim());
XmlNodeList mainTableNodes = doc.SelectNodes("configuration/TableMaps/MainTable");
DataTable dtSourceMain = new DataTable();
using (OracleConnection conn = new OracleConnection(SourceConnectString))
{
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
StringBuilder sbSql = new StringBuilder();
String srcTableName;
String srcKey;
String destTableName;
String destKey;
if (mainTableNodes.Count < 1)
{
MessageBox.Show("配置文件不对,缺少主表!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
srcTableName = mainTableNodes[0].Attributes["SrcTableName"].Value;
srcKey = mainTableNodes[0].Attributes["SrcKey"].Value;
destTableName = mainTableNodes[0].Attributes["DestTableName"].Value;
DeleteTargetTable(destTableName);
destKey = mainTableNodes[0].Attributes["DestKey"].Value;
sbSql.Append("select ");
foreach (XmlNode node in mainTableNodes[0])
sbSql.AppendFormat("{0}, ", node.Attributes["SqlText"].Value);
//sbSql.Remove(sbSql.ToString().LastIndexOf(","), 1);
sbSql.Append(srcKey);
sbSql.AppendFormat(" from {0}", srcTableName);
cmd.CommandText = sbSql.ToString();
OracleDataAdapter adapter = new OracleDataAdapter(cmd);
adapter.Fill(dtSourceMain);
dtSourceMain.Columns.Add(destKey, Type.GetType("System.Int32"));
for (int i = 0; i < dtSourceMain.Rows.Count; i++)
{
dtSourceMain.Rows[i][destKey] = i + 1;
}
using (SqlBulkCopy sqlBC = new SqlBulkCopy(TargetConnectString, SqlBulkCopyOptions.UseInternalTransaction))
{
sqlBC.BatchSize = 1000;
sqlBC.BulkCopyTimeout = 300;
sqlBC.DestinationTableName = destTableName;
//主表数据插入
sqlBC.ColumnMappings.Add(destKey, destKey);
foreach (XmlNode childNode in mainTableNodes[0].ChildNodes)
{
sqlBC.ColumnMappings.Add(childNode.Attributes["SrcColumn"].Value, childNode.Attributes["DestColumn"].Value);
}
sqlBC.WriteToServer(dtSourceMain);
//准备子表数据
XmlNodeList subTableNodes = doc.SelectNodes("configuration/TableMaps/SubTable");
progressBar.Maximum = subTableNodes.Count;
this.progressBar.Value = 1;
this.progressBar.Step = 1;
this.progressBar.PerformStep();
String srcForeignKey;
String destForeignKey;
foreach (XmlNode node in subTableNodes)
{
this.progressBar.PerformStep();
DataTable dtSourceSub = new DataTable();
srcTableName = node.Attributes["SrcTableName"].Value;
srcForeignKey = node.Attributes["SrcForeignKey"].Value;
destTableName = node.Attributes["DestTableName"].Value;
DeleteTargetTable(destTableName);
destForeignKey = node.Attributes["DestForeignKey"].Value;
sqlBC.DestinationTableName = destTableName;
sqlBC.ColumnMappings.Clear();
sqlBC.ColumnMappings.Add(destForeignKey, destForeignKey);
sbSql = new StringBuilder();
sbSql.Append("select ");
foreach (XmlNode childnodes in node.ChildNodes)
{
sbSql.AppendFormat("{0}, ", childnodes.Attributes["SqlText"].Value);
sqlBC.ColumnMappings.Add(childnodes.Attributes["SrcColumn"].Value, childnodes.Attributes["DestColumn"].Value);
}
sbSql.Append(srcForeignKey);
sbSql.AppendFormat(" from {0}", srcTableName);
cmd.CommandText = sbSql.ToString();
adapter = new OracleDataAdapter(cmd);
adapter.Fill(dtSourceSub);
dtSourceSub.Columns.Add(destForeignKey, Type.GetType("System.Int32"));
for (int i = 0; i < dtSourceSub.Rows.Count; i++)
{
DataRow[] drs = dtSourceMain.Select(String.Format("{0}='{1}'", srcKey, dtSourceSub.Rows[i][srcForeignKey]));
if (drs.Length > 0)
dtSourceSub.Rows[i][destForeignKey] = drs[0][destKey];
}
sqlBC.WriteToServer(dtSourceSub);
}
}
}
MessageBox.Show("数据转换成功,请查看目标表数据", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
progressBar.Value = 0;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
备注
使用SqlBulkCopy 类,配置映射时,目标表各个字段的名称必须和数据库中的字段名称保持大小写一致。各个数据类型的转换的时候,必须在映射的DataTable已经转换好,最好是查询的SQL语句,已经转换好,相见配置文件。
插入的时候,目标表不能有重复主键,否则失败;每条记录的每个字段的实际值大小不能超过目标表,否则报错。
相关文章推荐
- sqlbulkcopy 使用DataTable作为数据源的数据类型问题--来自数据源的String类型的给定值不能转换为指定目标列的类型 uniqueidentifier
- 使用SqlBulkCopy将datatable数据批量导入sqlServer
- DataTable中使用 SqlBulkCopy进行大批量数据插入
- 使用SqlBulkCopy, 插入整个DataTable中的所有数据到指定数据库中
- 使用SqlBulkCopy把DataTable里的数据插入数据表
- C#使用SqlBulkCopy将DataTable写入数据库的表中(表不存在则创建新表,数据存在则更新,不存在则插入)
- 使用SqlBulkCopy将DataTable中的数据批量插入数据库中
- 使用SqlBulkCopy将DataTable中的数据批量插入数据库中
- SqlBulkCopy 快速插入数据到SqlServer 数据库
- 使用SqlBulkCopy将DataTable中的数据批量插入数据库中
- IBatis.Net 下使用SqlBulkCopy 大批量导入数据 问题解决
- 【转载】使用事务和SqlBulkCopy导入大批量数据
- 使用SQLBULKCOPY提高导入数据的性能
- 批量插入数据 C# SqlBulkCopy使用
- 使用SqlBulkCopy进行大批量数据迁移
- C# 使用 SqlBulkCopy 类批量复制数据到数据库
- Sql Server数据库之通过SqlBulkCopy快速插入大量数据
- SQLBulkCopy使用实例--读取Excel写入数据库/将 Excel 文件转成 DataTable
- EF结合SqlBulkCopy在项目中的使用
- 使用事务和SqlBulkCopy导入大批量数据