2014-04-04 86 views
0

我知道這個問題是問了很多次,但我的問題是一個稍微不同的一個:加速讀取文本文件到sql表的最佳方法?

我要求如下:

我有一個文本文件,大約50000行和該文件差不多是10 MB。

每一行都有值具有固定尺寸如下:

0001   abcdefgh   1234567   xyz 
0002   pqrst    02233   abc 
003    asddfd   545    slfkk 

它不是一個製表符分隔或逗號分隔等

我有一個SQL表與如下相同的結構:

code varchar(5) 
name varchar(18) 
phone varchar(16) 
address varchar(20) 

我必須完成的是當客戶點擊按鈕時,它應該清空sql表並從文本文件中讀取新記錄(因爲這是一個ASP.net應用程序ication,我使用WCF服務來讀取文件並將字符串傳遞給服務器,並且它的工作正常)。

用於分離和保存到數據庫的代碼如下:

 Service1Client client = new Service1Client(); 
     string data = client.ReadFile(@"C:\testfolder\test.txt"); 


     context.Database.ExecuteSqlCommand(@"DELETE FROM table1"); 

     table1 c; 


     using (StringReader reader = new StringReader(data)) 
     { 
      string line = ""; 

      while ((line = reader.ReadLine()) != null) 
      { 
       c = new table1(); 
       c.Code = line.Substring(0, 5).Trim(); 
       c.name= line.Substring(5, 18).Trim(); 
       c.phone= line.Substring(23, 16).Trim(); 
       c.address = line.Substring(39, 20).Trim(); 
       context.table1.Add(c); 
      } 
     } 
     context.SaveChanges(); 
    } 
    client.Close(); 

但這種方法需要至少30分鐘複製所有記錄。有沒有更好的方法來加速這一點?

謝謝

+2

並使用SqlBulkCopy。我敢打賭,沒有時間用於閱讀 - 只有幾秒鐘。在table1.Add中使用了這個時間(AddRows在很多行中變慢)和SaveChanges(10.000個插入),而不是在讀取循環中。 – TomTom

+1

是的,我只是注意到它已經在內存中。雖然 – Johan

+0

我可以做的是對批量插入達成一致,但他可以對該代碼塊執行性能分析,以確定性能瓶頸所在,然後向我們報告我們可以在哪裏嘗試對其進行優化。 – Johan

回答

1

作爲一名數據庫負責人,我打算說「BCP」,但是......使用SqlBulkCopy。見文章在這裏:

http://msdn.microsoft.com/en-us/library/7ek5da1a%28v=vs.110%29.aspx

「微軟SQL Server包含名爲bcp的快速批量複製大文件到SQL Server數據庫表或視圖一個流行的命令行實用程序SqlBulkCopy類可以編寫託管代碼。提供類似功能的解決方案,還有其他方法可以將數據加載到SQL Server表中(例如,INSERT語句),但SqlBulkCopy比它們具有顯着的性能優勢。「

+0

你是對的! SqlBulkCopy是要走的路......只需不到5秒鐘就能完成整個過程!非常感謝 – sony

+0

:-)很高興能有所幫助。我自己在那裏。 – kpollock

0

您一次只讀一行,效率極低。您應該使用BufferedStream來代替:

緩衝區是內存中用於緩存數據的字節塊,因此可減少對操作系統的調用次數。緩衝區讀寫性能提高 。緩衝區可用於讀取或寫入,但不能同時寫入。 BufferedStream的讀寫方法 自動維護緩衝區。

+0

在我們的案例中,我們需要精確讀取一行數據,因爲它是一個聲明行或EDI行。緩衝區或字節塊可能難以鋸齒狀記錄長度。好的,記錄長度相同。是的,從這個觀點來看,一次一行是必要的。 – PCPGMR

0

做中介的SaveChanges並更換背景下由一個新問題:

context.Configuration.AutoDetectChangesEnabled = false; 
    int lines = 0; 
    int batchSize=50; 
    using (StringReader reader = new StringReader(data)) 
    { 
     string line = ""; 

     while ((line = reader.ReadLine()) != null) 
     { 
      c = new table1(); 
      c.Code = line.Substring(0, 5).Trim(); 
      c.name= line.Substring(5, 18).Trim(); 
      c.phone= line.Substring(23, 16).Trim(); 
      c.address = line.Substring(39, 20).Trim(); 
      context.table1.Add(c); 

      if (lines++ % batchSize == 0) 
      { 
       context.SaveChanges(); 
       context = new DataContext(); 
       context.Configuration.AutoDetectChangesEnabled = false; 
      } 
     } 
    } 
    context.SaveChanges(); 

可以調整BATCHSIZE

+0

除了可能重新連接到數據庫之外,創建新的上下文會爲您做什麼?這對我來說似乎沒有必要。 –

+0

@ TMcKeown釋放上下文使用的內存。爲什麼?我不知道,但這是我觀察到的。 – Gregoire

+0

嗯,有趣。我唯一擔心的是它可能使用的潛在重新連接時間。絕對是測試/測量的東西。 –

1

讀取該文件沒有什麼需要時間,這是插入到D B。 我將修改它通過以下方式,而不是執行更新一次全部做到在較小的批次:

你可以通過改變你多少行插入到一批if語句來測試性能。

using (StringReader reader = new StringReader(data)) 
    { 
     string line = ""; 
     int counter = 0; 
     while ((line = reader.ReadLine()) != null) 
     { 
      c = new table1(); 
      c.Code = line.Substring(0, 5).Trim(); 
      c.name= line.Substring(5, 18).Trim(); 
      c.phone= line.Substring(23, 16).Trim(); 
      c.address = line.Substring(39, 20).Trim(); 
      context.table1.Add(c); 
      if (counter == 10){ 
       context.SaveChanges(); 
       counter =0; 
      } 
      else{ 
       counter++; 
      } 
     } 
     context.SaveChanges(); 
    } 

或者,你可以設計一個producer/consumer模型,其中的文件是獨立於數據庫插入的閱讀,所以在本質上,你將有2個線程運行,1個線程讀取和其他插入到數據庫中。將多個線程插入數據庫不會有助於讀取獨立於數據庫插入的內容,從而提高整體性能。

相關問題