有没有比使用 DataTable 更快的方法来使用 SqlBulkCopy?
Is there a faster way to use SqlBulkCopy than using a DataTable?
我将大量记录加载到我的应用程序中(超过 100 万条)并对它们进行大量处理。处理要求它们都在内存中。
之后,我想将所有(现在已修改的)记录转储到一个空的 table。
加载记录只需几秒钟,我最终得到了一大堆 MyRecord
项。
使用 SqlBulkCopy
保存也只需几秒钟。
但是 SqlBulkCopy
需要(我相信)DataTable
- 将我的记录加载到 DataTable
很慢 - 使用
每分钟大约 7500 条记录
dataTable.Rows.Add(myRecord.Name, myRecord.Age, ....)
有没有更快的方法来执行这个中间步骤?
我不知道是什么问题。下面的程序运行不到一秒钟。我怀疑速度慢是由于读取数据而不是写入数据表造成的。
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Col A", typeof(int));
dt.Columns.Add("Col B", typeof(string));
dt.Columns.Add("Col C", typeof(int));
dt.Columns.Add("Col D", typeof(string));
dt.Columns.Add("Col E", typeof(int));
dt.Columns.Add("Col F", typeof(string));
dt.Columns.Add("Col G", typeof(int));
dt.Columns.Add("Col H", typeof(string));
dt.Columns.Add("Col I", typeof(int));
dt.Columns.Add("Col J", typeof(string));
DateTime begin = DateTime.Now;
for (int i = 0; i < 7500; i++)
{
dt.Rows.Add(new object[] {
i + 10000, "b", i + 20000, "d", i + 30000, "f", i + 40000, "h", i + 50000, "i"
});
}
DateTime end = DateTime.Now;
Console.WriteLine((end - begin).ToString());
Console.ReadLine();
}
造成延迟的原因是您必须先将所有内容缓冲到 DataTable 中,然后再将其发送到服务器。为了获得更好的性能,您应该立即将记录发送到 SqlBulkCopy,并让 class 使用它自己的缓冲和批处理。
SqlBulkCopy 可以与 IDataReader 一起使用。所有 ADO.NET 数据 reader 实现此接口,因此您可以将您从任何数据 reader 读取的数据推送到 SqlBulkCopy。
在其他情况下,假设您有一个 IEnumerable 对象,您可以使用 FastMember 包中的 Marc Gravel 的 ObjectReader 在 IEnumerable 之上创建一个 IDataReader。此数据 reader 不会 一次加载所有内容,因此在 SqlBulkCopy 请求之前不会缓存任何数据:
复制 Marc Gravel 的示例:
IEnumerable<SomeType> data = ...
using(var bcp = new SqlBulkCopy(connection))
using(var reader = ObjectReader.Create(data, "Id", "Name", "Description"))
{
bcp.DestinationTableName = "SomeTable";
bcp.WriteToServer(reader);
}
我将大量记录加载到我的应用程序中(超过 100 万条)并对它们进行大量处理。处理要求它们都在内存中。
之后,我想将所有(现在已修改的)记录转储到一个空的 table。
加载记录只需几秒钟,我最终得到了一大堆 MyRecord
项。
使用 SqlBulkCopy
保存也只需几秒钟。
但是 SqlBulkCopy
需要(我相信)DataTable
- 将我的记录加载到 DataTable
很慢 - 使用
dataTable.Rows.Add(myRecord.Name, myRecord.Age, ....)
有没有更快的方法来执行这个中间步骤?
我不知道是什么问题。下面的程序运行不到一秒钟。我怀疑速度慢是由于读取数据而不是写入数据表造成的。
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Col A", typeof(int));
dt.Columns.Add("Col B", typeof(string));
dt.Columns.Add("Col C", typeof(int));
dt.Columns.Add("Col D", typeof(string));
dt.Columns.Add("Col E", typeof(int));
dt.Columns.Add("Col F", typeof(string));
dt.Columns.Add("Col G", typeof(int));
dt.Columns.Add("Col H", typeof(string));
dt.Columns.Add("Col I", typeof(int));
dt.Columns.Add("Col J", typeof(string));
DateTime begin = DateTime.Now;
for (int i = 0; i < 7500; i++)
{
dt.Rows.Add(new object[] {
i + 10000, "b", i + 20000, "d", i + 30000, "f", i + 40000, "h", i + 50000, "i"
});
}
DateTime end = DateTime.Now;
Console.WriteLine((end - begin).ToString());
Console.ReadLine();
}
造成延迟的原因是您必须先将所有内容缓冲到 DataTable 中,然后再将其发送到服务器。为了获得更好的性能,您应该立即将记录发送到 SqlBulkCopy,并让 class 使用它自己的缓冲和批处理。
SqlBulkCopy 可以与 IDataReader 一起使用。所有 ADO.NET 数据 reader 实现此接口,因此您可以将您从任何数据 reader 读取的数据推送到 SqlBulkCopy。
在其他情况下,假设您有一个 IEnumerable 对象,您可以使用 FastMember 包中的 Marc Gravel 的 ObjectReader 在 IEnumerable 之上创建一个 IDataReader。此数据 reader 不会 一次加载所有内容,因此在 SqlBulkCopy 请求之前不会缓存任何数据:
复制 Marc Gravel 的示例:
IEnumerable<SomeType> data = ...
using(var bcp = new SqlBulkCopy(connection))
using(var reader = ObjectReader.Create(data, "Id", "Name", "Description"))
{
bcp.DestinationTableName = "SomeTable";
bcp.WriteToServer(reader);
}