我正在使用LinqToSql从C#向SqlServer 2008 Express DB插入大量记录。看起来插入速度非常慢。以下是代码段。
public void InsertData(int id)
{
MyDataContext dc = new MyDataContext();
List<Item> 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。
根据所有建议,我将此操作移动到部分(也是需求更改和设计决策),并以小块检索数据,并在数据库到达时将其插入数据库。我已将此InsertData()方法放在线程操作中,现在使用SmartThreadPool创建一个包含25个线程的池来执行相同的操作。在这种情况下,我一次只插入100条记录。现在,当我尝试使用Linq或sql查询时,它在时间方面没有任何区别。
根据我的要求,此操作计划每小时运行一次,并为大约4k-6k用户提取记录。所以,现在我将每个用户数据(检索和插入到数据库)操作汇集为一个任务并分配给一个线程。现在整个过程大约需要45分钟,大约有250,000条记录。
有没有更好的方法来完成这种任务?或者任何人都可以建议我如何改进这个过程?
答案 0 :(得分:12)
用于在大量数据中插入大量数据
Linq或SqlCommand,neither are designed for bulk copying data into SQL。
您可以使用SqlBulkCopy class提供对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
答案 1 :(得分:3)
如果要插入大量数据,可以尝试使用BULK INSERT。
据我所知,Linq to SQL中没有等效的批量插入。
答案 2 :(得分:3)
你有SubmitChanges()
被召唤一次,这很好。这意味着只使用了一个连接和事务。
请考虑重构代码以改为使用InsertAllOnSubmit()
。
List<dbItem> newItems = GetItems(id).Select(x=> new DbItem{ItemNo = x.No,
ItemName=x.Name})
.ToList();
db.InsertAllOnSubmit(newItems);
dc.SubmitChanges();
INSERT语句像以前一样逐个发送,但也许这可能更具可读性?
要问/考虑的其他一些事情:
context.Log = Console.Out;
与output your LINQ To SQL statements to the console。尽管如此,首选SQL Profiler。答案 3 :(得分:1)
以下是如何向应用程序添加Bulk-Insert类的一个很好的演练,这极大地提高了使用LINQ插入记录的性能。
(提供所有源代码,随时可以添加到您自己的应用程序中。)
http://www.mikesknowledgebase.com/pages/LINQ/InsertAndDeletes.htm
您只需要对代码进行三次更改,并在提供的类中进行链接。 祝你好运!