在工作中经常遇到要导入数据的场景。
1 导入全新的数据
2 导入的数据中库中可能已经存在,已经存在的数据不能覆盖,不能变更
3 导入的数据中库中可能已经存在,已经存在的数据主键等不能变化,同时需要更新这些数据的一些字段(比如:积分字段)
第一种最简单,最坑爹的是第三种了。
新手碰到这种问题往往一筹莫展,能想到的最直接的办法就是 一条条获取 ,然后判断,然后 update。这种方式的效率在少量数据的时候还没太大问题,当达到上千上万条时候问题就会出现了。
这种处理方式的资源开销和效率简直“惨绝人寰”。我也经历过这种方式,并渐渐的了解是应用了更高效的方式。
一、一条条的插入/获取判断和更新
这种方式上文已经说了效率和资源开销都是最大的,没啥好讲的,做程序的都会
二、一次插入多条/一次更新多条
插入多条:即一个connection.open() 后执行多个 command命令 。参考代码如下:
1 public static void ExecuteSqlTran(List<string> SQLStringList)//SQLs 2 { 3 using (SqlConnection conn = new SqlConnection(connectionString)) 4 { 5 conn.Open(); 6 SqlCommand cmd = new SqlCommand(); 7 cmd.Connection = conn; 8 SqlTransaction tx = conn.BeginTransaction(); 9 cmd.Transaction = tx; 10 try 11 { 12 for (int n = 0; n < SQLStringList.Count; n++) 13 { 14 string strsql = SQLStringList[n]; 15 if (strsql.Trim().Length > 1) 16 { 17 cmd.CommandText = strsql; 18 cmd.ExecuteNonQuery(); 19 } 20 } 21 tx.Commit(); 22 } 23 catch (System.Data.SqlClient.SqlException E) 24 { 25 tx.Rollback(); 26 throw new Exception(E.Message); 27 } 28 } 29 }
这种的方式效率会比 一 高很多,但是资源开销和执行效率还是难以让人忍受,在一次执行语句数量达到万级时候就表现的很明显。
我工作中的一次案例数据库中数据有2300w条记录,要对比的数据有20w条记录时候 纯update语句 1w条数据时候需要等待100s左右,当要对比的数据为40w条时候需要等待时间是600s左右。这种方式在效率上是相当让人无法忍受的。
三、使用DataSet更新记录
参考代码如下:
1 /// <summary> 2 /// 利用dataset批量更新数据 3 /// </summary> 4 /// <param name="sqlString"></param> 5 6 public void BatchUpDataForDataset(string sqlString) 7 { 8 using (SqlConnection connection = new SqlConnection(connectionString)) 9 { 10 11 12 using (SqlDataAdapter da = new SqlDataAdapter(sqlString, connection)) 13 { 14 DataSet ds = new DataSet(); 15 try 16 { 17 da.Fill(ds); 18 //交给委托处理 19 if (DataUpWork != null) 20 { 21 DataUpWork(ds); 22 } 23 24 SqlCommandBuilder scb = new SqlCommandBuilder(da); 25 //执行更新 26 da.InsertCommand = scb.GetUpdateCommand(); 27 da.Update(ds); 28 //使DataTable保存更新 29 ds.AcceptChanges(); 30 31 } 32 catch (System.Data.SqlClient.SqlException ex) 33 { 34 throw new Exception(ex.Message); 35 } 36 } 37 } 38 }
在代码中我使用了委托DataUpWork来处理dataset中的数据,随后提交。
这种方式写代码可能会轻松很多,但是效率也是不尽人意,好像最终也是生成update语句批量执行的,具体没有细细研究,请知道的大神指出这种方式的工作原理。
这种方式同样不能同时执行插入数据的操作(个人没测试过,不知道对DataSet新增的数据会不会插入到数据库中。(/ □ \))
四、 使用SqlBulkCopy批量插入全新的数据
这个批量插入效率真的极高,插入10w也是瞬间完成,参考代码
1 /// <summary> 2 /// 使用原列列表、目标列表和目标表明及数据记录数在指定的时间将数据批量导入到数据库 3 /// </summary> 4 /// <param name="sourceColumnName">源列name</param> 5 /// <param name="dbTableColumnName">目标列name</param> 6 /// <param name="tableName">目标表名</param> 7 /// <param name="sourceDt">数据源</param> 8 /// <param name="timeOut">指定时间</param> 9 public static void BatchInput(List<string> sourceColumnName, List<string> dbTableColumnName, string tableName, DataTable sourceDt, int timeOut = 300 ,string exSql="") 10 { 11 if (sourceColumnName.Count != dbTableColumnName.Count) 12 { 13 throw new Exception("传递的量表数据数不匹配"); 14 } 15 16 17 SqlConnection sqlConn = new SqlConnection(connectionString); 18 SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConn); 19 bulkCopy.BulkCopyTimeout = timeOut; 20 21 //目标表 22 if (string.IsNullOrEmpty(tableName)) 23 { 24 throw new ArgumentNullException(" 无效的目标表名:" + tableName); 25 } 26 27 bulkCopy.DestinationTableName = tableName; 28 29 30 31 for (int i = 0; i < sourceColumnName.Count; i++) 32 { 33 //原列和目标列 34 bulkCopy.ColumnMappings.Add(sourceColumnName[i], dbTableColumnName[i]); 35 36 } 37 //导入的数据量 38 bulkCopy.BatchSize = sourceDt.Rows.Count; 39 40 try 41 { 42 sqlConn.Open(); 43 if (!string.IsNullOrEmpty(exSql)) 44 { 45 SqlCommand cmd = new SqlCommand(exSql, sqlConn); 46 cmd.ExecuteNonQuery(); 47 } 48 49 if (bulkCopy.BatchSize != 0) 50 { 51 bulkCopy.WriteToServer(sourceDt); 52 } 53 } 54 catch (Exception ex) 55 { 56 throw ex; 57 } 58 finally 59 { 60 61 //释放资源 62 63 if (bulkCopy != null) 64 { 65 bulkCopy.Close(); 66 } 67 68 sqlConn.Close(); 69 } 70 71 72 }
但是该方法只能插入全新的记录,对于已经存在的数据无能为力了,不会去除已经存在的记录项,更不能更新已经存在的记录项