在C#中手动填充DataTable的最快方法

3
我想知道是否有更快的方法来手动填充DataTable,比我现在的方法更快。
这是我的代码,我有一个大约有17亿条目的列表。我想尽可能快地将这些条目填充到只有一列的DataTable中。我的列表中的条目看起来像这样{"A2C","DDF","ER","SQ","8G"}
我的代码需要大约7-8秒钟。
for (int i = 0; i <= lists.Count; i++)
{
    table_list.Rows.Add();
}

for (int a = 0; a < list.Count; a++)
{
    table_list.Rows[a][0] = list[a][0] + list[a][1] + 
        list[a][2] + list[a][3] + list[a][4];
}

由于没有在板块中找到类似的问题(只有关于如何通过 SQL 和填充方法填充数据表的问题),因此我决定发布我的问题。

非常感谢任何提供的帮助!


1
这是我得到的,我有一个大约有17亿条目的列表。我想尽快将这些条目填充到只有一列的DataTable中。但我必须问:为什么?你要用这个DataTable做什么?因为到目前为止,这听起来像一个非常糟糕的想法。根据您接下来想要做什么:可能会有更有效的方法。 - Marc Gravell
我通过SqlBulkCopy将这个DataTable添加到一个SQL Server数据库中。 - user3868224
K;我也这么认为:不要那样做!我会发布一个回答... - Marc Gravell
3个回答

7
我将这个DataTable添加到了SQL Server数据库中(通过SqlBulkCopy实现)。
这是一个错误;在这里,DataTable是纯粹的开销。你应该暴露一个IDataReader来操作这些数据。这个API有点棘手,但FastMember可以使它更容易。例如,看起来你只有1列;所以考虑:
class Foo {
    public string ColumnName {get;set;}
}

现在编写一个“迭代器块”方法,将其从原始列表转换为每个项目:
IEnumerable<Foo> Convert(List<TheOldType> list) {
    foreach(var row in list) {
        yield return new Foo { ColumnName = /* TODO */ };
    }
}

现在通过FastMember在懒惰序列之上创建一个IDataReader

List<TheOldType> list
var data = Convert(list);
using(var bcp = new SqlBulkCopy(connection))
using(var reader = ObjectReader.Create(data, "ColumnName"))
{
    bcp.DestinationTableName = "SomeTable";
    bcp.WriteToServer(reader);
}

这比填充 DataTable 要好得多 - 特别是避免了填充一个巨大的 DataTable。强调:以上是流式传输 - 不是缓冲传输。

你的解决方案让我再多得到一秒钟时间。谢谢! - user3868224
这个答案太棒了!谢谢。我们至少花费几秒钟的时间在代码中填充数据表,而实际上数据已经全部存在,只是格式不正确。 - Steztric
马克,我已经实现了这个版本(使用不同的EnumeratorDataReader),并且列表中有200万条记录,但只有最后的50万条被插入到Sql中。你有任何想法是为什么吗?我将bactcsize设置为0,但没有帮助。 - ManInMoon

4
首先创建一个空行,然后再遍历表格来填充它们是为什么呢?我建议使用简单的foreach语句:
var table_list = new DataTable();
table_list.Columns.Add();
foreach(string[] fields in lists)
{
    DataRow newRow = table_list.Rows.Add();
    newRow.SetField(0, string.Join("", fields));
}

为什么把所有内容都放在一个字段中?

我收到了一个IndexOutOfRangeException,消息是:未找到列0。由于我不能像以前那样简单地添加一列,所以我无法解决这个问题。 - user3868224
@user3868224:你如何初始化DataTable?当然,你需要添加一列。我已经编辑了我的答案来向你展示如何操作。 - Tim Schmelter
我忘记做那个了 ;) 你的代码需要6.2秒,有没有什么办法可以让它更快? - user3868224
@user3868224:我想知道为什么你会在意一两秒钟的时间,如果你想要在内存中填充一个有17亿行的表格,然后通过SqlBulkCopy填充数据库表格,这又需要一些时间。没有人期望它会更快。如果有人等待所有数据插入完成,你应该考虑在后台进行操作,并在完成后通知用户。这样用户可以同时做其他事情。你需要多经常插入这么多行?这篇文章值得一读:http://ericlippert.com/2012/12/17/performance-rant/ - Tim Schmelter

2
为什么不使用DataTable的LoadDataRow方法。
// turnoff notifications
table_list.BeginLoadData();

// load each row into the table
foreach(string[] fields in lists)
    table_list.LoadDataRow(new object[] { string.Join("", fields) }, false);

// turn notifications back on
table_list.EndLoadData();

同时参见:DataTable.LoadDataRow 方法 http://msdn.microsoft.com/en-us/library/kcy03ww2(v=vs.110).aspx


您的代码需要6.2秒,您知道有更快的方法吗? - user3868224
我不知道有什么更快的方法了,也许可以直接添加它 -> foreach(string[] fields in lists) table_list.Rows.Add(new object[] { string.Join("", fields) }, false); 但是我猜并不会有太大的差异。我认为最快的方法是不使用 DataTable。你能解释一下为什么要把它放进 DataTable 吗? - Jeroen van Langen
我需要尽快将我的数据添加到SQL Server中。我通过使用SqlBulkCopy和这个DataTable来实现。 - user3868224
使用.WriteToServer(DataTable)方法?导入时最耗时间的是什么?您是否正在测量转换为DataTable所需的时间? - Jeroen van Langen
我认为创建自己的 IDataReader 实现可以解决问题。问题在于,您需要将数据加载两次。从源到 DataTable,再从 DataTable 到批量复制。如果您直接通过自己的 IDataReader 将源数据提供给批量复制,您将提高性能.. (使用 WriteToServer(IDataReader) 方法) - Jeroen van Langen
显示剩余4条评论

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