1. 程式人生 > >SqlDataAdapter 更新插入 與 InsertBulkCopy

SqlDataAdapter 更新插入 與 InsertBulkCopy

item upd 由於 pub with style id字段 action 項目

近日做項目,涉及多個數據庫多個表的關聯更新,因數據量巨大,逐條更新也很費時。於是就想用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。終於搞定了。

SqlDataAdapter 更新插入 與 InsertBulkCopy