SQLServer批量更新、批量插入

最近遇到這樣一個問題,項目中幾個地方存儲的地址只有一個字段,省市區縣道路都在一起,地區檢索用like,這簡直是無法忍受,經商討決定對原地址字段進行拆分,但是數據量相對較大,其中最少的一張表中有70萬的數據,多的將近千萬,下面開啓拆分之路:
由於地址沒有什麼規律性,純SQL語句不能滿足需要,基本思路是程序配合SQL來實現。
方案一、最容易想到的,也是最開始想到的就是,把數據一行一行的取出來,經過運算、比對,找到省市縣之後,再根據id把數據更新回去。具體方法不再實現,效率極低。頻繁的數據庫打開、關閉、讀寫,操作測試庫都不能忍受,正式庫上高併發,極容易超時出錯。
方案二、上面是一條語句一條語句執行的,比它效率高點的是,一次性生成一定數量的SQL語句,放在一個list中,然後利用事務一次提交。這樣做可以大幅度減少數據庫建立連接、關閉連接的消耗,但是一次執行幾百條或者幾千條的SQL語句,磁盤讀寫量還是很大的,而且如果一條數據超時或者出錯,則事務回滾,這個也是不能忍的。
就實驗來說,上面兩種方案耗費的時間都相差不多,70萬條的數據差不多得一上午的時間去執行,那將近一千萬的怎麼活?查了很多資料,找到一很簡潔的方法。
方案三:
1、在SQLserver上建立一個自定義表類型(在本例中簡稱表變量,此表變量非存儲過程中聲明的@tablename),其中 有三個字段,id,省,市

 CREATE TYPE [dbo].[adress_update] AS TABLE(
	        [id] [INT] NULL,
	        [province] [VARCHAR](20) NULL,
	        [city] [VARCHAR](40) NULL
	        )

2、創建存儲過程(寫SQL也可以)

 CREATE PROCEDURE [dbo].[proc_update_address]
       @addresstable adress_update READONLY 
       AS
       BEGIN
       UPDATE TableName SET province=b.province,city=b.city
       FROM @addresstable b WHERE b.id=bizinfo.id
       END

這個存儲過程只有 一個參數,類型就是剛纔建的那個自定義表類型,語句很簡單就是 一個批量更新,看到這裏,很多人都差不多明白了,就是把一個表作爲一個變量來傳遞 ,好了,看最後的程序實現
3、程序實現,用的控制檯程序,個人感覺控制檯程序簡潔、方便

try
{
     DataTable dt = new DataTable();
     dt.Columns.Add("Id", typeof(int));
     dt.Columns.Add("province", typeof(string));
     dt.Columns.Add("city", typeof(string));                
     DataTable dtAddress = DBHelper.GetTable("SELECCT  Id,Address,Unit FROM TableName WHERE Address<>'' AND Address IS NOT NULL ");
     for (int i = 0; i < dtAddress.Rows.Count; i++)
     {
	     string province = "";
	     string city = "";
	     .
	     .
	     //把表dtAddress中的數據進行拆分組合出一個新的DataTable,此處省去具體的拆分步驟
	     DataRow dr = dt.NewRow();
         dr["Id"] = dtAddress.Rows[i]["Id"].ToString();
         dr["province"] = province;
         dr["city"] = city;
         dt.Rows.Add(dr);

     }
     SqlParameter[] param = { new SqlParameter("@addresstable", dt) };                
     DBHelper.RunProcedureNo("proc_update_address", param);//執行存儲過程,不返回結果                
 }
 catch (Exception ex)
 {
     Console.WriteLine(ex.Message);
 }

思路很簡單,就是把一張表作爲一個變量傳遞過去,從而實現批量更新,
這裏是把DataTable作爲一個參數傳遞過去的,這個就比較厲害了,這樣的話可以實現跨數據庫、跨服務器去更新、刪除、插入數據,MySQL,MSSQL,Orical等數據庫到MSSQL隨意的操作,簡直是不能太方便。
最後看看效率,做了這麼多,寫了這麼,沒有效率的話什麼都不是,在實際項目中,用方案三更新一萬條數據,花費時間不到一秒鐘。加上地址拆分的運算,70萬條數據的表更新完畢,耗時4分鐘左右(主要是地址拆分耗費的,不然也就不到1分鐘),完爆上面的兩個方案,數據插入的操作類似,不再贅述。

除此之外,還遇到一個問題
DataTable dt = new DataTable();
dt.Columns.Add(“Id”, typeof(int));
dt.Columns.Add(“province”, typeof(string));
dt.Columns.Add(“city”, typeof(string));
這塊代碼如果按下面這樣寫,就會報錯(報的錯誤大概內容是不能把字符串轉換爲數字)
DataTable dt = new DataTable();
dt.Columns.Add(“province”, typeof(string));
dt.Columns.Add(“city”, typeof(string));
dt.Columns.Add(“Id”, typeof(int));
就是更改了一下table列的添加順序,仔細查看錯誤提示,似乎是把province的數據存儲或者更新到了id的字段上,求知道原因的大神解惑。

補充上面問題的答案:
C#代碼中的DataTable dt的字段跟表變量adress_update中的字段沒有對應關係的,只需要類型一樣,順序一樣,即DataTable的第一列對應表變量的第一列,以此類推。所以,上面的代碼
DataTable dt = new DataTable();
dt.Columns.Add(“Id”, typeof(int));
dt.Columns.Add(“province”, typeof(string));
dt.Columns.Add(“city”, typeof(string));
也可以改爲:
DataTable dt = new DataTable();
dt.Columns.Add(“a”, typeof(int));
dt.Columns.Add(“b”, typeof(string));
dt.Columns.Add(“c”, typeof(string));

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