2014-04-04 50 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个线程读取和其他插入到数据库中。将多个线程插入数据库不会有助于读取独立于数据库插入的内容,从而提高整体性能。

相关问题