• SqlBulkCopy批量导入错误数据异常处理


      最近有个需求,将excel中的数据大批量导入到Sql Server中,方法很简单,使用SqlBulkCopy

    即可,网上介绍的方法也比较多,这里只是介绍一下对于数据的异常处理。

      由于项目是放在服务中运行,出现错误的数据希望只是希望将数据单独保存下来,不去影响其他数据的导入,网上找了一圈,没有找到合适的办法,只好自己仔细研究了一下,然后看到了SqlRowsCopied事件,MSDN中解释:在每次处理完 NotifyAfter 属性指定的行数时发生。如果将NotifyAfter设置为1的话,每次都记录一下当前执行到的行数,当出现异常的时候,可以定位到出错的行数。当知道了出错的行数,剩下的事情就很好办了,将出错的行数据单独拿出另存处理,计算一下已经正确导入到数据库中的数据行,移除出现,将剩下的数据继续导入。

      

      1 /// <summary>
      2     /// 导入数据,使用SqlBulkCopy
      3     /// </summary>
      4     public class SqlServerHelp : IDisposable
      5     {
      6         SqlConnection conn = null;
      7         SqlBulkCopy bulkCopy = null;
      8 
      9         private static string GetConnectionString()
     10         {
     11             return ConfigurationManager.AppSettings["SqlConnection"];
     12         }
     13 
     14         int batchSize = 1000;//批处理数据条数
     15         int bulkCopyTimeout = 3600;
     16 
     17         public SqlServerHelp()
     18         {
     19             string connectionString = GetConnectionString();
     20             conn = new SqlConnection(connectionString);
     21             Open();
     22             bulkCopy = new SqlBulkCopy(conn);
     23             bulkCopy.BatchSize = batchSize;
     24             bulkCopy.BulkCopyTimeout = bulkCopyTimeout;
     25             bulkCopy.NotifyAfter = 1;
     26             bulkCopy.SqlRowsCopied += new SqlRowsCopiedEventHandler(SqlRowsCopied);
     27         }
     28         
     29         public void Dispose()
     30         {
     31             this.Close();
     32         }
     33 
     34         public void Close()
     35         {
     36             data = null;
     37             if (conn != null && conn.State != ConnectionState.Closed)
     38                 conn.Close();
     39             if (bulkCopy != null)
     40                 bulkCopy.Close();
     41         }
     42 
     43         public void Import(BaseImportData importdata)
     44         {
     45             if (importdata.CanImport)
     46             {
     47                 if (importdata.DtSource == null || importdata.DtSource.Rows.Count == 0)
     48                 {
     49                 }
     50                 else
     51                 {
     52                     #region
     53                     bulkCopy.DestinationTableName = importdata.TableName;
     54                     bulkCopy.ColumnMappings.Clear();
     55 
     56                     foreach (ObjectColumn column in importdata.columns)
     57                     {
     58                         bulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping(
     59                             column.ExcelColumn, column.SqlServerColumn));
     60                     }
     61 
     62                     foreach (ObjectColumn column in importdata.defaultColumns)
     63                     {
     64                         bulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping(
     65                             column.ExcelColumn, column.SqlServerColumn));
     66                     }
     67 
     68                     try
     69                     {
     70                         data = importdata;
     71                         rowsCopiedId = 0;
     72                         indexRe = 0;
     73                         Open();
     74                         bulkCopy.WriteToServer(importdata.DtSource);
     75                     }
     76                     catch (SqlException ex)
     77                     {
     78                         DealSqlException(importdata, ex);
     79                     }
     80                     catch (InvalidOperationException ex)
     81                     {
     82                         DealExceptionNone(importdata, ex);
     83                     }
     84                     catch (Exception ex)
     85                     {
     86                         DealException(importdata, ex);
     87                     }
     88                     #endregion
     89                 }
     90             }
     91         }
     92 
     93         /// <summary>
     94         /// 数据导入完成之后导入日志以及错误信息
     95         /// </summary>
     96         /// <param name="importdata"></param>
     97         public void ImportLog(BaseImportData importdata)
     98         {
     99             data = importdata;
    100             rowsCopiedId = 0;
    101             indexRe = 0;
    102             InsertImportLog(importdata);
    103 
    104             foreach (ErrorInfo error in importdata.errorList)
    105             {
    106                 InsertErrorInfo(error);
    107             }
    108         }
    109 
    110         private void ImportError(BaseImportData importdata)
    111         {
    112             try
    113             {
    114                 data = importdata;
    115                 rowsCopiedId = 0;
    116                 indexRe = 0;
    117                 Open();
    118                 bulkCopy.WriteToServer(importdata.DtSource);
    119             }
    120             catch (SqlException ex)
    121             {
    122                 DealSqlException(importdata, ex);
    123             }
    124             catch (InvalidOperationException ex)
    125             {
    126                 DealExceptionNone(importdata, ex);
    127             }
    128             catch (Exception ex)
    129             {
    130                 DealException(importdata, ex);
    131             }
    132         }
    133 
    134         private void DealExceptionNone(BaseImportData importdata, Exception ex)
    135         {
    136             //错误行索引
    137             int errorIndex = Convert.ToInt32(rowsCopiedId);
    138 
    139             int succIndex = (errorIndex / batchSize) * batchSize;//成功导入到数据库中的数据最大索引
    140             //移除掉导入成功的数据
    141             for (int i = 0; i < succIndex; i++)
    142             {
    143                 importdata.DtSource.Rows.RemoveAt(0);
    144             }
    145             rowsCopiedId = 0;
    146 
    147             Logger.Info("插入数据出现异常:" + ex.GetType() + ex.Message);
    148             
    149             this.ImportError(importdata);
    150         }
    151 
    152         /// <summary>
    153         /// 如果出现异常,进行处理
    154         /// </summary>
    155         private void DealSqlException(BaseImportData importdata, SqlException ex)
    156         {
    157             switch (ex.Number)
    158             {
    159                 case -1:
    160                 case -2:
    161                 case 2:
    162                 case 53:
    163                     //数据库连接不上,超时等,隔一段时间后重新连接
    164                     //错误行索引
    165                     int errorIndex = Convert.ToInt32(rowsCopiedId);
    166 
    167                     int succIndex = (errorIndex / batchSize) * batchSize;//成功导入到数据库中的数据最大索引
    168                     //移除掉导入成功的数据
    169                     for (int i = 0; i < succIndex; i++)
    170                     {
    171                         importdata.DtSource.Rows.RemoveAt(0);
    172                     }
    173                     rowsCopiedId = 0;
    174 
    175                     Logger.Info("导入数据异常:" + ex.Message);
    176 
    177                     System.Threading.Thread.Sleep(600000);
    178 
    179                     this.ImportError(importdata);
    180                     break;
    181                 case 10053:
    182                 case 64:
    183                     DealExceptionNone(importdata, ex);
    184                     break;
    185                 default:
    186                     //其余的异常认为是数据行出错
    187                     DealException(importdata, ex);
    188                     break;
    189             }
    190         }
    191 
    192         /// <summary>
    193         /// 如果出现异常,进行处理
    194         /// </summary>
    195         private void DealException(BaseImportData importdata, Exception ex)
    196         {
    197             importdata.ImportResult = "存在错误数据";
    198 
    199             //错误行索引
    200             int errorIndex = Convert.ToInt32(rowsCopiedId);
    201             int succIndex = (errorIndex / batchSize) * batchSize;//成功导入到数据库中的数据最大索引
    202             //long errorNum = rowsNum + errorIndex + 1;
    203             
    204             ErrorInfo error = new ErrorInfo(importdata);
    205             if (ex is SqlException)
    206             {
    207                 error.ErrorDesc = ex.GetType() + ((SqlException)ex).Number.ToString() + ex.Message;
    208             }
    209             else
    210                 error.ErrorDesc = ex.GetType() + ex.Message;
    211 
    212             error.SetContent(importdata.DtSource.Rows[errorIndex]);
    213 
    214             //error.Remark = importdata.DtSource.TableName.Replace("'", "").Replace("$", "") + "中,第" + (
    215             //    errorNum + 1).ToString() + "行";
    216             error.Remark = importdata.DtSource.TableName.Replace("'", "").Replace("$", "") + "中,第" + (
    217                importdata.DtSource.Rows[errorIndex]["ExcelRowNum"]).ToString() + "";
    218             importdata.errorList.Add(error);
    219 
    220             //rowsNum = rowsNum + succIndex + 1;
    221 
    222             Logger.Error("存在错误数据:" + error.Remark + error.ErrorDesc);//记录异常数据
    223 
    224             importdata.DtSource.Rows.RemoveAt(errorIndex);//删除异常数据
    225 
    226             for (int i = 0; i < succIndex; i++)
    227             {
    228                 importdata.DtSource.Rows.RemoveAt(0);
    229             }
    230 
    231             rowsCopiedId = 0;
    232             this.ImportError(importdata);
    233         }
    234 
    235         //当前批量复制操作期间复制的行数
    236         long rowsCopiedId = 0;
    237         //成功导入到数据库中行数
    238         //long rowsNum = 0;
    239         long indexRe = 0;//已经批量执行到数据库中的次数
    240         BaseImportData data;
    241         private void SqlRowsCopied(object sender, SqlRowsCopiedEventArgs e)
    242         {
    243             //错误行索引
    244             int errorIndex = Convert.ToInt32(rowsCopiedId);
    245             int succIndex = (errorIndex / batchSize) * batchSize;//成功导入到数据库中的数据最大索引
    246             
    247             rowsCopiedId = e.RowsCopied;
    248             if ((e.RowsCopied -1) / batchSize > indexRe)
    249             {
    250                 Logger.Debug("已经成功导入" + succIndex + "行,excel中第" + data.DtSource.Rows[succIndex]["ExcelRowNum"].ToString() + "");
    251                 indexRe++;
    252             }
    253             else if (e.RowsCopied == data.DtSource.Rows.Count)
    254             {
    255                 Logger.Debug("已经成功导入" + e.RowsCopied + "行,excel中第" + data.DtSource.Rows[succIndex]["ExcelRowNum"].ToString() + "");
    256                 indexRe++;
    257             }
    258         }
    259 
    260         /// <summary>
    261         /// 插入错误信息
    262         /// </summary>
    263         public void InsertErrorInfo(ErrorInfo info)
    264         {
    265                     }
    266 
    267         /// <summary>
    268         /// 插入导入日志
    269         /// </summary>
    270         public void InsertImportLog(BaseImportData importdata)
    271         {
    272             
    273         }
    274 
    275         /// <summary>
    276         /// 修改导入日志
    277         /// </summary>
    278         public void UpdateImportLog(BaseImportData importdata)
    279         {
    280             
    281         }
    282 
    283         public DataTable GetImportLog(string _dataType)
    284         {
    285             return null;
    286         }
    287 
    288         
    289         public void Open()
    290         {
    291             if (conn.State == ConnectionState.Closed)
    292             {
    293                 try
    294                 {
    295                     conn.Open();
    296                 }
    297                 catch (SqlException ex)
    298                 {
    299                     Logger.Debug("打开数据库连接异常:" + ex.GetType() + ex.Number + ex.Message);
    300                     System.Threading.Thread.Sleep(600000);
    301                     this.Open();
    302                 }
    303                 catch (Exception ex)
    304                 {
    305                     Logger.Debug("打开数据库连接异常:" + ex.GetType() + ex.Message);
    306                     System.Threading.Thread.Sleep(600000);
    307                     this.Open();
    308                 }
    309             }
    310         }
    311 
    312 
    313         private DataTable QueryTable(string sql)
    314         {
    315             Open();
    316             SqlCommand comm = new SqlCommand();
    317             comm.Connection = conn;
    318             comm.CommandType = CommandType.Text;
    319             try
    320             {
    321                 comm.CommandText = sql;
    322                 DataSet ds = new DataSet();
    323                 SqlDataAdapter da = new SqlDataAdapter(comm);
    324                 da.Fill(ds);
    325                 if (ds.Tables.Count > 0)
    326                     return ds.Tables[0];
    327             }
    328             catch (SqlException ex)
    329             {
    330                 switch (ex.Number)
    331                 {
    332                     case -1:
    333                     case -2:
    334                     case 2:
    335                     case 53:
    336                     case 10053:
    337                     case 64:
    338                         Logger.Debug("网络故障:执行sql查询出错:" + ex.GetType() + ex.Message);
    339                         return this.QueryTable(sql);
    340                     default:
    341                         throw;
    342                 }
    343             }
    344             catch (InvalidOperationException ex)
    345             {
    346                 Logger.Debug("网络故障:执行sql出错:" + ex.GetType() + ex.Message);
    347                 return this.QueryTable(sql);
    348             }
    349             catch (Exception ex)
    350             {
    351                 throw;
    352             }
    353             return null;
    354         }
    355 
    356         private void ExecuteNonQuery(string sql)
    357         {
    358             Open();
    359             SqlCommand comm = new SqlCommand();
    360             comm.Connection = conn;
    361             comm.CommandType = CommandType.Text;
    362             try
    363             {
    364                 comm.CommandText = sql;
    365                 comm.ExecuteNonQuery();
    366             }
    367             catch (SqlException ex)
    368             {
    369                 switch (ex.Number)
    370                 {
    371                     case -1:
    372                     case -2:
    373                     case 2:
    374                     case 53:
    375                     case 10053:
    376                     case 64:
    377                         Logger.Debug("网络故障:执行sql出错:" + ex.GetType() + ex.Message);
    378                         this.ExecuteNonQuery(sql);
    379                         break;
    380                     default:
    381                         throw;
    382                 }
    383             }
    384             catch (InvalidOperationException ex)
    385             {
    386                 Logger.Debug("网络故障:执行sql出错:" + ex.GetType() + ex.Message);
    387                 this.ExecuteNonQuery(sql);
    388             }
    389             catch (Exception ex)
    390             {
    391                 throw;
    392             }
    393         }
    394 
    395     }    
    View Code

      第一次发文,写的比较散乱,希望可以给有相同需求的人一些启示。

  • 相关阅读:
    Python_soket
    Python_正则表达式语法
    Python_math模块
    Python_random模块
    Python_os模块
    Python_time模块
    Java技能树-图片版
    读书笔记---《编写可读代码的艺术》
    Java代码优化建议
    Git常用命令
  • 原文地址:https://www.cnblogs.com/xutt/p/3369600.html
Copyright © 2020-2023  润新知