有比使用DataTable更快的SqlBulkCopy方法吗?

4
我将大量记录(100万+)加载到我的应用程序中,并对它们进行大量处理。处理需要将它们全部保留在内存中。
之后,我想将所有(现在修改后的)记录转储到空表中。
加载记录只需几秒钟,然后我就得到了一个包含大量" MyRecord "项目的数组。
使用SqlBulkCopy进行保存也只需几秒钟。
但是SqlBulkCopy需要(我认为)一个 DataTable - 将我的记录加载到 DataTable 中很慢 - 每分钟约7500条记录。
dataTable.Rows.Add(myRecord.Name, myRecord.Age, ....)

有没有更快的方法执行这个中间步骤?

他的例子是创建新行并添加数据,这也是我正在做的事情,但速度非常慢。我不确定为什么他要使用反射,因为你不需要这样做。 - NibblyPig
1
SQLBulkCopy和DataTable的性能问题? - Caius Jard
是的,请查找如何使用XML批量插入数据库。 - MethodMan
1
删除此内容,因为在查看这些答案后发现问题是基准测试代码中出现了一个简单的查找错误。 - NibblyPig
1
@MethodMan 关于“简单而棒的过程”,我现在正在从8个不同的系统中加载数据——包括数据库、FTP、S3文件、IATA HOT文件(特殊解析)、屏幕抓取、Web服务(如XML)、REST服务,并在SSIS数据流中进行匹配以查找差异。我甚至使用TPL Dataflow来解析文本文件,转换它们并将它们插入到数据库中,这样做所需的时间与将它们写入文本文件以进行导入所需的时间相同。TPL Dataflow还允许我并行处理多个请求,例如同时处理10个REST查询。 - Panagiotis Kanavos
显示剩余7条评论
2个回答

12

延迟是由于在将数据发送到服务器之前必须将所有内容缓冲到 DataTable 中。为了获得更好的性能,您应该立即将记录发送到 SqlBulkCopy,并让该类使用其自己的缓冲和批处理。

SqlBulkCopy 可以使用 IDataReader 进行操作。所有 ADO.NET 数据读取器都实现了此接口,因此您可以将从任何数据读取器中读取的数据推送到 SqlBulkCopy。

在其他情况下,假设您有一个包含对象的 IEnumerable,您可以使用 Marc Gravel 的 ObjectReader(来自FastMember包)在 IEnumerable 顶部创建一个 IDataReader。这个数据读取器并不会一次加载所有数据,因此除非 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); 
}

我使用BlockingCollection作为SqlBulkCopy和读取/反序列化过程之间的中介,实现了一个快速的DbDataReader派生类。问题是,读取/反序列化json的步骤比SqlBulkCopy提前太多,导致内存占用量巨大,处理时间增加了一倍。本来希望它比使用DataTable的“缓冲和批处理方法”更快速和内存高效,但结果却相反。 - undefined
如果你使用Dataflow blocks,例如TransformBlock或TransformManyBlock来解析JSON为对象,使用BatchBlock将这些对象分组为批次,再使用ObjectReader和SqlBulkCopy插入批次,那么批处理将变得更加容易。 - undefined

0

我不知道问题出在哪里。下面的程序运行时间不到一秒钟。我怀疑慢速是由于读取数据而不是写入到 DataTable 导致的。

       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();
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接