SqlDataAdapter 更新插入 与 InsertBulkCopy

近日做项目,涉及多个数据库多个表的关联更新,因数据量巨大,逐条更新也很费时。于是就想用SqlDataAdapter 一次提交一批数据过去。以下是自己经历的坑:

1. table Merge 部分

 DataTable dtCBBill = DbHelper.ExecuteDataAdapter(SqlHelper.cbBill, pars, strConnOldCBBill);
 DataTable dtMember = DbHelper.ExecuteDataAdapter(SqlHelper.accounts_m, null, strConnMember);
 DataTable dtUser = DbHelper.ExecuteDataAdapter(SqlHelper.accounts_u2, null, strConnWeb);
 //筛选出有账单的用户
 DataTable dtM = (from c in dtMember.AsEnumerable()
                   join r in dtCBBill.AsEnumerable() on c["f_accounts"] equals r["f_accounts"]
                   select c).CopyToDataTable();
 DataTable dtU = (from c in dtUser.AsEnumerable()
                   join r in dtCBBill.AsEnumerable() on c["f_accounts"] equals r["f_accounts"]
                   select c).CopyToDataTable();
 //设置主键加快合并速度
 SetPrimaryKey(dtCBBill);
 SetPrimaryKey(dtM);
 SetPrimaryKey(dtU);
 MergeTable(dtM, dtCBBill);
 MergeTable(dtU, dtCBBill);。。。。dtContribution

Merge提示<target>.f_CBMoney 和 <source>.f_CBMoney 的属性冲突: DataType 属性不匹配。几经调试,发现,源dtCBBill的一个字段是decimal,而对应合并的目的表dtContribution相应字段是int 类型。修改sql语句,cast(某字段 as int) 解决此类异常。网上有说

在调用dt1.Merge(dt2)的时候,由于两个serverid字段类型不一致,一个int32,一个int64,导致无法Merge。用importRow的方式就可以合并了:

private DataTable MergeTable(DataTable dest, DataTable source)
{
      DataRow[] sourceRows = source.Select();
      for (int i = 0; i < sourceRows.Length; i++)
      {
          dest.ImportRow(sourceRows[i]);
       }
       return dest;
 }

这种方法需要改动代码多,就没有尝试。
2. InsertBulkCopy 部分

public static int InsertBulkCopy(string connectionString, DataTable dt,string destTable)
{
    using (SqlConnection Connection = new SqlConnection(connectionString))
    {
        Connection.Open();
        using (SqlTransaction transaction = Connection.BeginTransaction())
        {
            using (SqlBulkCopy sqlbulkcopy = new SqlBulkCopy((SqlConnection)Connection, SqlBulkCopyOptions.KeepIdentity, transaction))
            {
               sqlbulkcopy.DestinationTableName = destTable;
               for (int i = 0; i < dt.Columns.Count; i++)
               {
                   sqlbulkcopy.ColumnMappings.Add(dt.Columns[i].ColumnName, dt.Columns[i].ColumnName);
                }
                sqlbulkcopy.BatchSize = 10000;
                try
                {
                    sqlbulkcopy.WriteToServer(dt);
                    transaction.Commit();
                    return 1;
                 }
                 catch(Exception ex)
                 {
                    transaction.Rollback();
                    return 0;
                 }
              }
        }
    }
}

必须要保证 source table 与 destination table 字段完全一致,包括字段的大小写。因为大小写不一样,如 f_employee 与 f_Employee,直接会导致插入失败。

3. 最重要的 SqlDataAdapter

SqlDataAdapter adapter = new SqlDataAdapter();
SqlConnection conn = new SqlConnection(strConnCash);
adapter.SelectCommand = new SqlCommand(SqlHelper.selCmdText, conn);
SqlParameter parameter1 = adapter.SelectCommand.Parameters.Add("@f_date", SqlDbType.DateTime);
parameter1.Value = date;

DataTable dtContri = new DataTable();
adapter.Fill(dtContri);

adapter.UpdateBatchSize = 1000;
adapter.UpdateCommand = new SqlCommand(SqlHelper.updCmdText, conn);
adapter.UpdateCommand.Parameters.Add("@f_CBMoney", SqlDbType.Int, 4, "f_CBMoney");
adapter.UpdateCommand.Parameters.Add("@f_CBMresult", SqlDbType.Int, 4, "f_CBMresult");
adapter.UpdateCommand.Parameters.Add("@f_CBNum", SqlDbType.Int, 4, "f_CBNum");
SqlParameter[] paramters = new SqlParameter[]
{
       new SqlParameter("@f_date",SqlDbType.DateTime,8,"f_date"),
       new SqlParameter("@f_accounts",SqlDbType.VarChar,20,"f_accounts")
};
paramters[0].SourceVersion = DataRowVersion.Original;
paramters[1].SourceVersion = DataRowVersion.Original;
                            adapter.UpdateCommand.Parameters.AddRange(paramters);
adapter.UpdateCommand.UpdatedRowSource = UpdateRowSource.None;

adapter.InsertCommand = new SqlCommand(SqlHelper.insCmdText, conn);
adapter.InsertCommand.Parameters.Add("@f_accounts", SqlDbType.VarChar, 20, "f_accounts");
adapter.InsertCommand.Parameters.Add("@f_date", SqlDbType.DateTime, 4, "f_date");
adapter.InsertCommand.Parameters.Add("@f_CBMoney", SqlDbType.Int, 4, "f_CBMoney");
adapter.InsertCommand.Parameters.Add("@f_CBMresult", SqlDbType.Int, 4, "f_CBMresult");
adapter.InsertCommand.Parameters.Add("@f_CBNum", SqlDbType.Int, 4, "f_CBNum");
                            adapter.InsertCommand.Parameters.Add("@f_StupeSurplus", SqlDbType.Int, 4, "f_StupeSurplus");
adapter.InsertCommand.Parameters.Add("@f_status", SqlDbType.TinyInt, 1, "f_status");
adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.None;

//将ds数据同步到dtContri表
SetPrimaryKey(dtContri);
MergeTableManual( dtCBBill,ref dtContri);                           

//执行更新插入
int r = adapter.Update(dtContri);
log.Info(site + " CB update&insert: " + r);
/// <summary>
/// 设置主键
/// </summary>
/// <param name="dt"></param>
private void SetPrimaryKey(DataTable dt)
{
   dt.PrimaryKey = new DataColumn[] { dt.Columns["f_accounts"] };
}

/// <summary>
/// 合并表数据
/// </summary>
/// <param name="source"></param>
/// <param name="target"></param>
private void MergeTable(DataTable source, DataTable target, bool isAddSchema = true)
{
    target.Merge(source, false, isAddSchema ? MissingSchemaAction.Add : MissingSchemaAction.Ignore);
}

/// <summary>
/// 手动合并表数据
/// </summary>
/// <param name="source"></param>
/// <param name="target"></param>
private void MergeTableManual(DataTable source,ref DataTable target)
{
   foreach (DataRow item in source.Rows)
   {
      var row = target.Rows.Find(item["f_accounts"]);
      if (row != null)
      {
         row["f_CBMoney"] = item["f_CBMoney"];
         row["f_CBMresult"] = item["f_CBMresult"];
         row["f_CBNum"] = item["f_CBNum"];
       }
       else
       {
          var newRow = target.NewRow();
          newRow["f_accounts"] = item["f_accounts"];
          newRow["f_date"] = item["f_date"];
          newRow["f_CBMoney"] = item["f_CBMoney"];
          newRow["f_CBMresult"] = item["f_CBMresult"];
          newRow["f_CBNum"] = item["f_CBNum"];
          newRow["f_StupeSurplus"] = item["f_StupeSurplus"];
          newRow["f_status"] = item["f_status"];
          target.Rows.Add(newRow);
        }
    }
}

这其中坑最多。按照 https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/updating-data-sources-with-dataadapters  这篇最有价值的文章,发现日志记录更新数目总是为0。 然后自己又做个demo,一点一点拆开研究,dataTable.SaveChanges 只是把源表最近的更新同步到离线表中,不可用。但离线表,取其中的一DataRow,改变下数据,甚至原数据再更新都能Update成功,最后通过打印RowState属性值,得到,用dataTable1.Merge(dataTable2,...)方式合并来的数据,RowState 为unchanged。遂手动Merge。运行,OK。终于搞定了。

时间: 2025-01-03 23:23:18

SqlDataAdapter 更新插入 与 InsertBulkCopy的相关文章

【VB】操作ODBC-DAO方式操作只能查询,不能更新插入操作解决。

最近接手一个改善项目,需要从Access转化到SQL Server 2014,使用原有的ODBC连接方式只能查询,不能更新插入.网上一直找不到解决方案,然后自己测试一下使用ADO方式竟然可以连接了.具体问题还未找到,现在贴出例子代码,希望给自己一个帮助找出真正问题所在. 1 Private Sub Command1_Click() 2 On Error GoTo Err 3 Dim db As Database 4 Dim Rs As Recordset 5 Dim Qry As QueryDe

MaxCompute在更新插入、直接加载、全量历史表中的数据转换实践

摘要: 2018"MaxCompute开发者交流"钉钉群直播分享,由阿里云数据技术专家彬甫带来以"MaxCompute数据仓库数据转换实践"为题的演讲.本文首先介绍了MaxCompute的数据架构和流程,其次介绍了ETL算法中的三大算法,即更新插入算法.直接加载算法.全量历史表算法,再次介绍了在OLTP系统中怎样处理NULL值,最后对ETL相关知识进行了详细地介绍. 2018"MaxCompute开发者交流"钉钉群直播分享,由阿里云数据技术专家彬

.net批量更新(插入、修改、删除)数据库

思路: 1. 设置DataTable中每行的状态标识,即调用DataRow的方法setAdded().setModified().Delete() 2. 使用DataAdapter的Update(DataTable)方法 代码例子: String connString = "........"; //连接字符串 String selectCommand = "select * from item"; SqlDataAdapter da = new SqlDataAd

SqlDataAdapter更新数据

插入.更新和删除的排序 在许多情况下,以何种顺序向数据源发送通过 DataSet 作出的更改是相当重要的. 例如,如果已更新现有行的主键值并且添加了具有新主键值的新行, 则务必要在处理插入之前处理更新. 可以使用 DataTable 的 Select 方法来返回仅引用具有特定 RowState 的 DataRow 数组. 然后可以将返回的 DataRow 数组传递到 DataAdapter 的 Update 方法来处理已修改的行. 通过指定要更新的行的子集,可以控制处理插入.更新和删除的顺序.

数据 更新 插入 删除

1.UPDATE实现对数据的更新操作,语法如下:①.更新单行数据:UPDATE dbtab SET f1=g1 ... fn=gn WHERE .f表组建字段名,g为新设定的值,WHERE为确保只更新单行.注:除f=g外还可 f=f+g.f=f-g通过工作区更改单行数据:UPDATE dbtab FROM wa.②.更新多行数据:UPDATE dbtab SET f1=g1 ... fi=gi [WHERE ].也可以使用SET和WHERE子句同时更新多行数据值:此外不需要在WHERE中限定所有

Oracle利用触发器更新插入时某一列的值

先创建一个表Brzl 其中Pym是Brxm的首字母拼音 新建触发器前简单备注下触发器创建方法 create [or replace] tigger 触发器名 触发时间 触发事件 on 表名 [for each row] begin pl/sql语句 end 其中: 触发器名:触发器对象的名称.由于触发器是数据库自动执行的,因此该名称只是一个名称,没有实质的用途. 触发时间:指明触发器何时执行,该值可取: before:表示在数据库动作之前触发器执行; after:表示在数据库动作之后触发器执行.

关于Hibernate级联更新插入信息时提示主键不为空的问题“org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1 ”

org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1 出现这一错误的主要原因有两个       使用的是hibernate的saveOrUpdate方法保存实例.saveOrUpdate方法要求ID为null时才执行SAVE,在其它情况下执行UPDATE.在保存实例的时候是新增,但你的ID不为null,

python往mysql数据库中写入数据和更新插入数据

本文链接:https://blog.csdn.net/Mr__lqy/article/details/85719603 1. 连接mysql import pymysql db = pymysql.connect(host='localhost', user='root', password='123456', port=3306, db='spiders') cursor = db.cursor() sql = 'select * from students;' cursor.execute(

python 判断更新插入数据库

1 #-*-coding:utf8-*- 2 #此处调试代码... 3 import sys 4 import os 5 import MySQLdb 6 import datetime 7 path1 = (os.getcwd()).split(r'\test')[0] 8 sys.path.append(path1+'\Shop_site_crawler') 9 from zsl_spider_lib import get_u_sql,get_s_sql,get_i_sql 10 impor