诗剑书生的专栏

我在灌江口上住,花开花落,不知流年度.雁过空遗秋色暮,抚琴细听梧桐雨. 轻舞残虹漫展书,云卷云舒,思愫万千缕.安得婵娟与共处,长作识字耕田夫.                   诗剑书生 于灌江口.听潮居

诗剑书生ID:axman
105594次访问,排名830好友0人,关注者8
一个男人. 一个写程序男人. 一个写程序并从程序中寻找快乐的男人. 一个写程序并从程序中寻找快乐又把快乐传递给大家的男人.
一个书生. 一个寂寞的书生. 一个寂寞的梅香竹影下敲声写韵的书生. 一个寂寞的梅香竹影下敲声写韵晨钟暮鼓中逸气扬剑的书生.
那个男人是位书生。没有人知道他的姓名,居无定所,行无定踪,亦耕亦读,或渔或樵。
axman的文章
原创 87 篇
翻译 0 篇
转载 0 篇
评论 162 篇
axman的公告
最近评论
axman:不用说明什么,一切都如我预料的那样.
在整个奥运开幕式期间,我一边看电视,一边访问
http://www.cctvolympics.com/main.php?type=vod这个地址,其间访问一百多次,没有一次成功.大多数时候是前端缓存的squid在报refuse(111).有时以过几分钟的等待,能出来页面,但Flash缓冲的进度条到了99%就一直停止在那儿.
……
myvicc:写得不错,等写下文
chinagavin:我是先读三位正向输出,然后再读三位反向输出来最终达到想要的结果。
chinagavin:更新啊,好久没更新了。
YuLimin:快更新呀。。。等着呢:)呵呵。。。
文章分类
收藏
    相册
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 在C#中完成海量数据的批量插入和更新收藏

    新一篇: HTTPS传送文件的打包方式(为一个朋友调试的过程) | 旧一篇: C#与.NET 3.0高级程序设计(特别版)问题与错误列表


    对于海量数据的插入和更新,ADO.NET确实不如JDBC做到好,JDBC有统一的模型来进行批操作.使用起来
    非常方便:
     PreparedStatement ps = conn.prepareStatement("insert or update arg1,args2....");
     然后你就可以
     for(int i=0;i<1000000000000000;i++){
      ps.setXXX(realArg);
      .....
      ps.addBatch();
      if(i%500==0){ //假设五百条提交一次
       ps.executeBatch();
       //clear Parame Batch
      }
     }
     ps.executeBatch();
     
    这样的操作不仅带来极度大的性能,而且非常方便.按说,ADO.NET中,要实现这样的功能,应该直接在Command接口中
    或DataAdapter接口中提供Addbat和CommitBat的API,但ADO.NET的却并没有这样简单地实现,而是要求开发者通过
    复杂的变通方法.
     对于大量的插入操作,可以利用一个空的DataTable加入要插入的行,达到一定数量提交后清空该表就行了,
    实现起来并不算复杂:

               

     

     

    DateTime begin = DateTime.Now;
    string connectionString = ......;
    using(SqlConnection conn = new SqlConnection(connectionString)){
        conn.Open();
        SqlDataAdapter sd 
    = new SqlDataAdapter();
        sd.SelectCommand 
    = new SqlCommand("select devid,data_time,data_value from CurrentTest", conn);
        sd.InsertCommand 
    = new SqlCommand("insert into CurrentTest (devid,data_time,data_value) "
                        
    + " values (@devid,@data_time,@data_value);", conn);
        sd.InsertCommand.Parameters.Add(
    "@devid", SqlDbType.Char, 18"devid");
        sd.InsertCommand.Parameters.Add(
    "@data_time", SqlDbType.Char, 19"data_time");
        sd.InsertCommand.Parameters.Add(
    "@data_value", SqlDbType.Int, 8"data_value");
        sd.InsertCommand.UpdatedRowSource 
    = UpdateRowSource.None;
        sd.UpdateBatchSize 
    = 0;

        DataSet dataset 
    = new DataSet();
        sd.Fill(dataset);
        Random r 
    = new Random(1000);
        
    for (int i = 0; i < 100000; i++{
            
    object[] row = {"DEVID"+i,DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),r.Next(1,1000) };
            dataset.Tables[
    0].Rows.Add(row);
            
    if (i % 300 == 0{
                sd.Update(dataset.Tables[
    0]);
                dataset.Tables[
    0].Clear();
            }

        }

        sd.Update(dataset.Tables[
    0]);
        dataset.Tables[
    0].Clear();
        sd.Dispose();
        dataset.Dispose();
        conn.Close();
       
    }

    TimeSpan ts 
    = DateTime.Now - begin;
    MessageBox.Show(
    "ts = " + ts.TotalMilliseconds);

     

    对于这个测试我插入10万条数据用时28秒.性能还算可圈可点.但是对于批量更新,搜遍全球的例子,都是把记录Fill到DataSet中然后牧举rows
    来更新,就我这个小数据量的测试而言,把10万条数据Fill到DataSet中已经不能工作,如果是百万,千万如何操作?难道一定先把要批操作的记录
    先获取到DataSet中?也就是我要更新哪些记录就要选查询这些记录?

     于是我仍然利用一个空的DataTable来加入要更新的记录:

     sd.SelectCommand = new SqlCommand("select devid,data_time,data_value from CurrentTest where 1=0", conn);
     //1=0的条件保证取一个空表.
     sd.UpdateCommand = new SqlCommand("update CurrentTest set data_time = @data_time,data_value = @data_value where devid = @devid", conn);
            sd.UpdateCommand.Parameters.Add("@data_time", SqlDbType.Char, 19, "data_time");
            sd.UpdateCommand.Parameters.Add("@data_value", SqlDbType.Int, 4, "data_value");
            sd.UpdateCommand.Parameters.Add("@devid", SqlDbType.Char, 20, "devid");
            sd.UpdateCommand.UpdatedRowSource = UpdateRowSource.None;
            sd.UpdateBatchSize = 0;

     for(int i=0;i<300;i++){
      ..............................
      dataset.Tables[0].Rows.Add(row);
     }
     sd.Update(dataset.Tables[0]);
     先更新300条试试,如果成功再循环更新所有记录,但提示插入操作需要InsertCommand,因为一个空表然后Add Row操作.这时RowState是Added,如果这时Update到数据库,执行的就是插入操作而无法更新.


     改成:
     for(int i=0;i<300;i++){
      ..............................

     row = {填入初始化的值};
      dataset.Tables[0].Rows.Add(row);
     }
     dataset.AcceptChanges();
     for(int i=0;i<300;i++){
      ..............................
      dataset.Tables[0].Rows[i][x] = "xxxxxxx";
      ..............................
     }
     sd.Update(dataset.Tables[0]);
     先在DataTable中插入数据,然后用AcceptChanges(),修改RowState为UnChanged,再修改表中数据希望改变UnChanged状态,即将DataTable从Current状态改为Original,然后再对DataTable的Row进行更新,就能使

    Update成功.但这样做确实不方便.


     调整思路,先从数据库中取200条(批更新的Size大小),直接得到一个Original的DataTable.
     sd.SelectCommand = new SqlCommand("select top 200 devid,data_time,data_value from CurrentTest", conn);
     DataSet dataset = new DataSet();
            sd.Fill(dataset);
     用这200个空间来放要更新的其它数据看看:
     
                        for (int i = 0; i < 100; i++)
                        {
                            dataset.Tables[0].Rows[i].BeginEdit();
                            dataset.Tables[0].Rows[i]["data_time"] = "2222-22-22 22:22:22";
                            dataset.Tables[0].Rows[i]["data_value"] = 100;
                            dataset.Tables[0].Rows[i]["devid"] = "DEVID"+(i+10000);//更新DEVID10000到DEVID10200的记录
                            dataset.Tables[0].Rows[i].EndEdit();
                        }
                        sd.Update(dataset.Tables[0]);
     OK,成功,哈哈.把要更新的数据不断往这个空间填,填满就提交,这样更新100000条数据只要几个循环就行了.


                       

     

    DateTime begin = DateTime.Now;
                
    string connectionString = "";
                
    using(SqlConnection conn = new SqlConnection(connectionString)){
                    conn.Open();

                    SqlDataAdapter sd 
    = new SqlDataAdapter();
                    sd.SelectCommand 
    = new SqlCommand("select top 200 devid,data_time,data_value from CurrentTest", conn);

                    DataSet dataset 
    = new DataSet();
                    sd.Fill(dataset);
                    Random r 
    = new Random(1000);

                    sd.UpdateCommand 
    = new SqlCommand("update CurrentTest "
                                    
    + " set data_time = @data_time,data_value = @data_value where devid = @devid", conn);
                    sd.UpdateCommand.Parameters.Add(
    "@data_time", SqlDbType.Char, 19"data_time");
                    sd.UpdateCommand.Parameters.Add(
    "@data_value", SqlDbType.Int, 4"data_value");
                    sd.UpdateCommand.Parameters.Add(
    "@devid", SqlDbType.Char, 20"devid");
                    sd.UpdateCommand.UpdatedRowSource 
    = UpdateRowSource.None;
                    sd.UpdateBatchSize 
    = 0;
                    
    for (int count = 0; count < 100000;)
                    
    {

                        
    for (int i = 0; i < 200; i++,count++)
                        
    {
                            dataset.Tables[
    0].Rows[i].BeginEdit();
                            dataset.Tables[
    0].Rows[i]["data_time"= "2222-22-22 22:22:22";
                            dataset.Tables[
    0].Rows[i]["data_value"= 100;
                            dataset.Tables[
    0].Rows[i]["devid"= "DEVID"+count;
                            dataset.Tables[
    0].Rows[i].EndEdit();
                        }

                        sd.Update(dataset.Tables[
    0]);
                    }

      

                    dataset.Tables[
    0].Clear();
                    sd.Dispose();
                    dataset.Dispose();
                    conn.Close();
                   
                }

                TimeSpan ts 
    = DateTime.Now - begin;
                MessageBox.Show(
    "ts = " + ts.TotalMilliseconds);

     注意上面的更新操作是指在一个十万,百万,千万条记录中我要不断更新其中的记录,这些要更新的记录并不是从头

    到尾这样的顺序,只是不断地根据条件更新任何记录,我不可能把成百上千万记录先Fill到ds中然后在ds中Select到

    这条记录然后更新,所以每200次更新操作填入一次DataTable中提交,就实现了JDBC的addBat和executeBat操作.

     这个操作更新10万条用了32秒,还算勉强吧.

     KAO,没有更优雅的方法了.只好将就这样用了.

    发表于 @ 2008年03月20日 18:45:00|评论(loading...)|编辑

    新一篇: HTTPS传送文件的打包方式(为一个朋友调试的过程) | 旧一篇: C#与.NET 3.0高级程序设计(特别版)问题与错误列表

    评论

    #iAudio 发表于2008-03-21 10:11:46  IP: 59.108.79.*
    其实微软早就封装好了大批量入库的方法,
    楼主可以用SqlBulkInsert,
    或者用BCP的BCP INSERT
    可看我的博客:http://cnblogs.com/meback
    #axman 发表于2008-03-21 10:46:42  IP: 61.135.207.*
    这挨得上边吗?一个是利用SqlServer来从文件导入大量数据,一个是应用程序要求SqlServer执行大量插入,另外BulkInsert能完成更新操作的批处理?
    更新操作的批处理不是批量更新,批量更新是update tb set col = val where col = val,这样一条语句可以更新多条记录叫批量更新,
    而更新操作的批处理就是多条更新语句一次提交给数据库完成.这根本不是BulkInsert能完成的.
    BulkInsert所完成的功能在10年前mysql就完成了.
    load data local infile xxxfile into table,而插入操作的批更新应用程序有多个插入操作要求一次提交给数据库.当我有一百万用户要注册时,难道我要先把用户注册信息写成格式化的文件中然后再调用 SqlBulkInsert导入注册的表中?对于我接收用户信息的程序来说只能是一次请求获得数据后执行一次insert操作,但每一次操作就提交一次的损性能,所以做批提交.SqlBulkInsert完全不是一回事.
    #sail988 发表于2008-03-22 20:27:47  IP: 221.130.252.*
    试了一下
    Sqlserver 速度还行,
    为啥在Oracle上试就贼慢呢?
    Sqlserver 10W 五十秒
    Oracle 1W 340秒
    #axman 发表于2008-03-22 20:59:40  IP: 123.119.66.*
    如果是oracle,建议使用JDBC操作.主要是OLE对象的交互影响性能,
    如果采用的MS的技术,最好就是在它自己的平台上,系统用windows server,容器用IIS,编码用C#/VB.NET,数据库用MS SQLSERVER,这样才能发挥它的性能.
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © axman