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

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

posted @ 2013-10-15 09:26  applextt  阅读(897)  评论(0编辑  收藏  举报