如何使用 SqlDataReader 像使用 SqlDataAdapter 一样快地填充 DataTable?

Posted

技术标签:

【中文标题】如何使用 SqlDataReader 像使用 SqlDataAdapter 一样快地填充 DataTable?【英文标题】:How can I fill a DataTable using a SqlDataReader as fast as when using SqlDataAdapter? 【发布时间】:2013-10-12 09:12:39 【问题描述】:

我写了一个方法,它在从IDataReader 源中获取后立即返回DataRow(使用yield 关键字)。它可以工作,但它确实比使用SqlDataAdapter 填充DataTable 慢。

public static IEnumerable<DataRow> AsEnumerable(this IDataReader source)

    //create columns
    DataTable dt = new DataTable(); 
    for (int i = 0; i < source.FieldCount; i++)
    
        dt.Columns.Add(source.GetName(i), source.GetFieldType(i));
    

    //fetch data
    object[] data = new object[source.FieldCount];   
    dt.BeginLoadData();
    while (source.Read())
    
        DataRow dr = dt.NewRow(); //this is very slow, bottleneck ?

        source.GetValues(data); 
        for (int i = 0; i < source.FieldCount; i++)
        
            dr[i] = data[i];
        
        dt.Rows.Add(dr);
        yield return dr;
    
    dt.EndLoadData();

这是结果(250.000 行,很多列):

00:08:34.652  //using SqlDataAdapter
00:12:95.153  //using method above

我怎样才能让它像使用 SqlDataAdapter 一样快?

为什么我使用它而不是SqlDataAdapter:我想在行来自源时立即处理它们,而不是“阻塞”很长时间并等待所有记录都在那里。

也许还有另一种方法可以做到这一点。注意:我不能简单地产生一个IDataReader(它很快并且支持我需要的东西:使用Item[ColumnName]访问数据,因为数据将被不同的线程访问,所以我需要在移动到下一个之前将它复制到某个地方行...

【问题讨论】:

也许只做 source.GetData(dr.ItemArray); 会更快 好建议。我要试试这个。在提出问题之前我尝试的是使用 DataRow.ItemArray[i] (在 for 循环内)填充 DataRow,它真的很慢。 它稍微快一点,我在 9.6 到 10.5 秒之间(很好),但它仍然总是比 SqlDataAdapter 慢(即使经过几次尝试) 【参考方案1】:

我会回答我自己的问题:

一种与使用SqlDataReader 性能相似的方法是使用DataTable.LoadDataRow(object[] values, bool fAcceptChanges) 方法。这是使用Fill() 方法填充DataTable 时内部使用的方法。

它很快,因为它通过直接访问一些内部字段来填充DataRow 数据,同时使用 DataRow 索引器(例如:dr[i] = value) 将进行多次内部检查。

正如 Lorentz 建议的那样,使用 ItemArray 比索引器快,但仍然比使用 LoadDataRow() 慢。

【讨论】:

以上是关于如何使用 SqlDataReader 像使用 SqlDataAdapter 一样快地填充 DataTable?的主要内容,如果未能解决你的问题,请参考以下文章

在 c# 中使用 SqlDataReader 填充 List<T>

如何使用 SqlDataReader 在结构数组中存储多个值

如何使用 SqlDataReader 返回和使用 IAsyncEnumerable

如何在 JavaScript 中使用 C# SQLDataReader 对象

如何在 C# 中使用 SqlDataReader 获取行数

如何使用 SqlDataReader 获取位值并将其转换为布尔值?