批量导入/更新 数据的几种方法

在工作中经常遇到要导入数据的场景。

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         }

但是该方法只能插入全新的记录,对于已经存在的数据无能为力了,不会去除已经存在的记录项,更不能更新已经存在的记录项 ??

五、使用临时表作为中介来进行导入和更新

这种方式效率极高,资源开销我只能说不是很大,开销主要在数据库上相对(本人技术菜不会监测开销情况),我工作中的一个案例:数据库表有2500w数据,需要导入处理的有15w数据,要对比的有45w历史数据。执行时间大约在30s内,就导入了新记录和更新了历史记录。

参考代码如下:

     /// <summary>
        /// 使用原列列表、目标列表和目标表明及数据记录数在指定的时间将数据批量导入到数据库
        /// </summary>
        /// <param name="sourceColumnName">源列name</param>
        /// <param name="dbTableColumnName">目标列name</param>
        /// <param name="tableName">目标表名</param>
        /// <param name="sourceDt">数据源</param>
        /// <param name="timeOut">指定时间</param>
        void BatchInput(List<string> sourceColumnName, List<string> dbTableColumnName, string tableName, DataTable sourceDt, out   int olduser, out  int newuser, int timeOut = 300, string exSql = "")
        {
            olduser = 0;
            newuser = 0;
            if (sourceColumnName.Count != dbTableColumnName.Count)
            {
                throw new Exception("传递的量表数据数不匹配");
            }
            string connectionString = ConfigurationManager.ConnectionStrings["sqlconn"].ToString();
            SqlConnection sqlConn = new SqlConnection(connectionString);
            SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConn);
            bulkCopy.BulkCopyTimeout = timeOut;

            //目标表
            if (string.IsNullOrEmpty(tableName))
            {
                throw new ArgumentNullException(" 无效的目标表名:" + tableName);
            }

            bulkCopy.DestinationTableName = tableName;

            for (int i = 0; i < sourceColumnName.Count; i++)
            {
                //原列和目标列
                bulkCopy.ColumnMappings.Add(sourceColumnName[i], dbTableColumnName[i]);

            }
            //导入的数据量
            bulkCopy.BatchSize = sourceDt.Rows.Count;
            sqlConn.Open();
            SqlTransaction transaction = null;

            try
            {

                SqlCommand cmd = sqlConn.CreateCommand();

                    cmd.CommandText =  create  table  #a( mobile  varchar(50),a  int ,  idno  int , b  int  ,c  int ,d  int )";    //创建临时表
                    cmd.ExecuteNonQuery();

                if (bulkCopy.BatchSize != 0)
                {
                    bulkCopy.WriteToServer(sourceDt);
                }

                transaction = sqlConn.BeginTransaction("SampleTransaction");

                cmd.Transaction = transaction;     //设置事务
                //更新老用户
                cmd.CommandText = " select   #a.*, UserTable.UserID  into  #cz  from   #a  left  join    UserTable     on   UserTable.a=4  and   UserTable.b=" + ActivityId + "  and    UserTable.c=#a.mobile  where   UserID  is  not null";    //创建老用户数据表
                cmd.ExecuteNonQuery();
                cmd.CommandText = " update       UserTable     set     b+=b,c=#cz.c     from    #cz ,UserTable  where  UserTable.a=4  and   UserTable.b=" + ActivityId + "  and  #cz.userid=UserTable.UserID  ";    //更新老用户数据
                cmd.ExecuteNonQuery();
                cmd.CommandText = " select   #a.*, UserTable.UserID  into  #newuser    from   #a  left  join    UserTable on   UserTable.a=4  and   UserTable.b=" + ActivityId + "  and    UserTable.c=#a.mobile where   UserID  is  null  ";    //创建新用户数据表
                cmd.ExecuteNonQuery();
                cmd.CommandText = " insert into    UserTable (a,b,[c],d,e,f)  select  a,b,[c],d,e,f  from  #newuser   ";    //插入老用户
                cmd.ExecuteNonQuery();

                cmd.CommandText = "  select  count(1) from  #newuser ";    //新用户数量
                newuser = (int)(cmd.ExecuteScalar());

                cmd.CommandText = "  select  count(1) from  #cz ";    //老用户数量
                olduser = (int)(cmd.ExecuteScalar());
                transaction.Commit();
                WriteMessage("导入完毕,共导入" + sourceDt.Rows.Count + "条记录,其中新增用户:" + newuser + "条,老用户:" + olduser + "条");

            }
            catch (Exception ex)
            {
                bulkCopy.Close();
                if (transaction != null)
                {
                    transaction.Rollback();
                }

                WriteMessage("反生错误,导入失败:" + ex.Message + "\r\n" + ex.StackTrace);
            }
            finally
            {
                sqlConn.Close();
            }

        }

原理: 首先创建临时表,然后用SqlBulkCopy 将数据批量导入到临时表中,但后用临时表  联合 数据库存表查询  得到老用户 数据,存储到临时表  #cz中,然后更新老用户的相关字段

在联合库存表查询出新用户存储到临时表 #newuser  中 ,然后将数据   insert into  select  库存表中

六、大杀器

如果只有全新的记录并且你又能接触到数据库那么请用excel等文件,直接用sql server 管理工具进行导入  /捂脸

时间: 2024-10-28 23:54:55

批量导入/更新 数据的几种方法的相关文章

SQL Server 批量插入数据的两种方法

在SQL Server 中插入一条数据使用Insert语句,但是如果想要批量插入一堆数据的话,循环使用Insert不仅效率低,而且会导致SQL一系统性能问题.下面介绍SQL Server支持的两种批量数据插入方法:Bulk和表值参数(Table-Valued Parameters). 运行下面的脚本,建立测试数据库和表值参数. [c-sharp] view plaincopy --Create DataBase create database BulkTestDB; go use BulkTes

将Excel数据导入mysql数据库的几种方法

将Excel数据导入mysql数据库的几种方法 “我的面试感悟”有奖征文大赛结果揭晓! 前几天需要将Excel表格中的数据导入到mysql数据库中,在网上查了半天,研究了半天,总结出以下几种方法,下面和大家分享一下: 一.用java来将Excel表格中的数据转到mysql中 这是我们用的第一种方法,就是在java找你感谢个类,然后这个类会将Excel表格中的数据存储到内存里,然后再从内存中读出来插入到数据库中,但是要 注意了,这里是存储到String[ ]数组里面,所以取出来的数据也是Strin

探讨android更新UI的几种方法

作为IT新手,总以为只要有时间,有精力,什么东西都能做出来.这种念头我也有过,但很快就熄灭了,因为现实是残酷的,就算一开始的时间和精力非常充足,也会随着项目的推进而逐步消磨殆尽.我们会发现,自己越来越消极怠工,只是在无意义的敲代码,敲的还是网上抄来的代码,如果不行,继续找. 这就是项目进度没有规划好而导致的. 最近在做有关蓝牙的项目,一开始的进度都安排得很顺利,但是因为测试需要两部手机,而且还要是android手机,暑假已经开始了,同学们都回家了,加上我手机的蓝牙坏了,导致我的进度严重被打乱!而

mysql大批量插入数据的4种方法示例

前言 本文主要给大家介绍了关于mysql大批量插入数据的4种方法,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 方法一:循环插入 这个也是最普通的方式,如果数据量不是很大,可以使用,但是每次都要消耗连接数据库的资源. 大致思维如下 (我这里写伪代码,具体编写可以结合自己的业务逻辑或者框架语法编写) ? 1 2 3 4 5 6 7 8 9 10 11 12 13 for($i=1;$i<=100;$i++){  $sql = 'insert...............';  /

Node.JS的表单提交及OnceIO中接受GET/POST数据的三种方法

OnceIO 是 OnceDoc 企业私有内容(文档)管理系统的底层Web框架,它可以实现模板文件.静态文件的全缓存,运行起来完全不需要I/O操作,并且支持客户端缓存优化,GZIP压缩等(只压缩一次),拥有非常好的性能,为您节约服务器成本.它的模块化功能,可以让你的Web进行分布式存储,在一个扩展包里即可包含前端.后端和数据库定义,只需通过添加/删除目录的方式就可实现功能删减,实现真正的模块化扩展.目前 OnceIO 已经开源,本文主要介绍node.js语言中的表单提交及OnceIO中接受GET

5.MVC框架开发(强类型开发,控制器向界面传递数据的几种方法)

界面表单中的表单元素名字和数据库表的字段名相一一映射(需要哪个表的数据就是那个表的模型(Model)) 在View页面中可以指定页面从属于哪个模型 注:以上的关系可以通过MVC的强类型视图开发来解决我们的开发代码的智能感知 1.强类型视图开发的步骤 1)在页面中指定强类型的类型(一定是引用类型),代码:例: @model List<MyMVCBookShop.Models.Book> 2)从控制器传强类型的数据到View视图里,那么就必须保证传入的数据一定和强类型视图中定义的类型相一致,例:

MySQL中删除数据的两种方法

转自:http://blog.csdn.net/apache6/article/details/2778878 在MySQL中有两种方法可以删除数据,一种是DELETE语句,另一种是TRUNCATE TABLE语句. DELETE语句可以通过WHERE对要删除的记录进行选择.而使用TRUNCATE TABLE将删除表中的所有记录.因此,DELETE语句更灵活. 如果要清空表中的所有记录,可以使用下面的两种方法: DELETE FROM table1 TRUNCATE TABLE table1 其

DataGridView显示数据的两种方法

1.简单介绍 DataGridView空间是我们常用的显示数据的控件,它有极高的可配置性和可扩展性. 2.显示数据 DataGridView显示数据一般我们常用的有两种方法,一种是直接设置DataSoure属性就可以绑定数据.此方法不需要写任何代码操作比较简单,但是它显示出来的是整张表的数据.如果整一表数据比较多,而且我们并不需要所有的数据的情况下,我们就应该考虑第二种方法了.通过写代码连接数据库并从数据库中读取数据,最后将返回的数据传给DataGridView.这种方法貌似比较复杂,但是它只显

Android 更新UI的两种方法——handler和runOnUiThread() - $firecat的代码足迹$ - 博客频道 - CSDN.NET

文章来源:http://www.2cto.com/kf/201302/190591.html Android 更新UI的两种方法——handler和runOnUiThread() 在Android开发过程中,常需要更新界面的UI.而更新UI是要主线程来更新的,即UI线程更新.如果在主线线程之外的线程中直接更新页面显示常会报错.抛出异常:android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread th