在C#中完成海量數據的批量插入和更新

對於海量數據的插入和更新,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,沒有更優雅的方法了.只好將就這樣用了.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章