使用Linq到Sql的插入过程非常慢

我正在使用LinqToSql从C#向SqlServer 2008 express DB插入大量记录。 看起来插入速度非常慢。 以下是代码段。

public void InsertData(int id) { MyDataContext dc = new MyDataContext(); List result = GetItems(id); foreach (var item in result) { DbItem dbItem = new DbItem(){ItemNo = item.No, ItemName=item.Name}; dc.Items.InsertOnSubmit(); } dc.SubmitChanges(); } 

我做错了吗? 或者使用Linq插入大量记录是一个糟糕的选择?

更新:感谢您的所有答案。 @ p.campbell:对于记录计数很抱歉,这是一个错字,实际上大约是100000.记录也一直到200k。

根据所有建议,我将此操作转换为部分(也是需求更改和设计决策),并以小块的forms检索数据,并在数据库到达时将其插入数据库。 我已将此InsertData()方法放在线程操作中,现在使用SmartThreadPool创建一个包含25个线程的池来执行相同的操作。 在这种情况下,我一次只插入100条记录。 现在,当我尝试使用Linq或sql查询时,它在时间方面没有任何区别。

根据我的要求,此操作计划每小时运行一次,并为大约4k-6k用户提取记录。 所以,现在我将每个用户数据(检索和插入到数据库)操作汇集为一个任务并分配给一个线程。 现在整个过程大约需要45分钟,大约有250,000条记录。

有没有更好的方法来完成这种任务? 或者任何人都可以建议我如何改进这个过程?

用于在oner中将大量数据插入SQL中

Linq或SqlCommand, 都不是为将数据批量复制到SQL而设计的 。

您可以使用SqlBulkCopy类 ,它提供对bcp实用程序的托管访问,以便从几乎任何数据源批量加载数据到Sql。

SqlBulkCopy类可用于仅将数据写入SQL Server表。 但是,数据源不仅限于SQL Server; 只要数据可以加载到DataTable实例或使用IDataReader实例读取,就可以使用任何数据源。

性能比较

即使从简单的CSV文件加载数据,SqlBulkCopy也是最快的。

Linq将在SQL中生成一堆Insert语句并将它们发送到SQL Server。 这与使用SqlCommand Ad-hoc查询没什么不同。 SqlCommand与Linq的性能几乎完全相同。

证据

(SQL Express 2008,.Net 4.0)

SqlBulkCopy的

使用SqlBulkCopy从CSV文件加载100000行(包括加载数据)

 using (SqlConnection conn = new SqlConnection("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=EffectCatalogue;Data Source=.\\SQLEXPRESS;")) { conn.Open(); Stopwatch watch = Stopwatch.StartNew(); string csvConnString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\data\\;Extended Properties='text;'"; OleDbDataAdapter oleda = new OleDbDataAdapter("SELECT * FROM [test.csv]", csvConnString); DataTable dt = new DataTable(); oleda.Fill(dt); using (SqlBulkCopy copy = new SqlBulkCopy(conn)) { copy.ColumnMappings.Add(0, 1); copy.ColumnMappings.Add(1, 2); copy.DestinationTableName = "dbo.Users"; copy.WriteToServer(dt); } Console.WriteLine("SqlBulkCopy: {0}", watch.Elapsed); } 

的SqlCommand

 using (SqlConnection conn = new SqlConnection("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=TestDb;Data Source=.\\SQLEXPRESS;")) { conn.Open(); Stopwatch watch = Stopwatch.StartNew(); SqlCommand comm = new SqlCommand("INSERT INTO Users (UserName, [Password]) VALUES ('Simon', 'Password')", conn); for (int i = 0; i < 100000; i++) { comm.ExecuteNonQuery(); } Console.WriteLine("SqlCommand: {0}", watch.Elapsed); } 

LinqToSql

 using (SqlConnection conn = new SqlConnection("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=TestDb;Data Source=.\\SQLEXPRESS;")) { conn.Open(); Stopwatch watch = Stopwatch.StartNew(); EffectCatalogueDataContext db = new EffectCatalogueDataContext(conn); for (int i = 0; i < 100000; i++) { User u = new User(); u.UserName = "Simon"; u.Password = "Password"; db.Users.InsertOnSubmit(u); } db.SubmitChanges(); Console.WriteLine("Linq: {0}", watch.Elapsed); } 

结果

 SqlBulkCopy: 00:00:02.90704339 SqlCommand: 00:00:50.4230604 Linq: 00:00:48.7702995 

如果要插入大量数据,可以尝试使用BULK INSERT 。

据我所知,在Linq to SQL中没有等效的批量插入。

你有一次调用SubmitChanges() ,这很好。 这意味着只使用一个连接和事务。

请考虑重构代码以使用InsertAllOnSubmit()

 List newItems = GetItems(id).Select(x=> new DbItem{ItemNo = x.No, ItemName=x.Name}) .ToList(); db.InsertAllOnSubmit(newItems); dc.SubmitChanges(); 

INSERT语句像以前一样逐个发送,但也许这可能更具可读性?

其他一些要问/考虑的事情:

  • 目标表上的索引状态是什么? 太多会减慢写入速度。 *数据库是简单还是完全恢复模式?
  • 捕获通过网络传输的SQL语句。 在针对SQL Server数据库的adhoc查询中重播这些语句。 我意识到你正在使用SQL Express,并且可能没有SQL Profiler。 使用context.Log = Console.Out; 将LINQ To SQL语句输出到控制台 。 尽管如此,首选SQL Profiler。
  • 捕获的SQL语句执行与客户端代码相同的操作吗? 如果是这样,则性能问题出现在数据库端。

下面是如何向应用程序添加Bulk-Insert类的一个很好的演练,这极大地提高了使用LINQ插入记录的性能。

(提供所有源代码,随时可以添加到您自己的应用程序中。)

http://www.mikesknowledgebase.com/pages/LINQ/InsertAndDeletes.htm

您只需要对代码进行三次更改,并在提供的类中进行链接。 祝好运 !