首页 > 解决方案 > 如何使用 DataTable 和 Linq 优化代码?

问题描述

我有 2 个数据表。大约有17000 (table1)100000 (table2)记录。

需要检查“FooName”字段是否包含“ItemName”。还需要获取“FooId”,然后将“ItemId”“FooId”添加到 ConcurrentDictionary。

我有这个代码。

DataTable table1;
DataTable table2;           
var table1Select = table1.Select();

ConcurrentDictionary<double, double> compareDictionary = new ConcurrentDictionary<double, double>();

foreach (var item in table1)
{
         var fooItem = from foo in table2.AsEnumerable()
                  where foo.Field<string>("FooName").Contains(item.Field<string>("ItemName"))
                  select foo.Field<double>("FooId");
         if(fooItem != null && fooItem.FirstOrDefault() != 0)
                {
                    compareDictionary.TryAdd(item.Field<double>("ItemId"), fooItem.FirstOrDefault());
                }
}

它工作缓慢(执行任务大约需要 10 分钟)。

我想让它更快。我该如何优化它?

标签: c#algorithmlinqdatatableconcurrentdictionary

解决方案


我看到你可以攻击三点:

  1. 放弃对字段访问器的强类型化以支持直接强制转换:它强制取消装箱,您可以完全避免使用doubles值类型。upd正如评论中指出的那样,无论哪种方式,您都不会避免拆箱,但可能会节省一些方法调用开销(这又是有争议的)。这一点大概可以忽略
  2. 缓存引用字符串,因此每个外循环只能访问一次
  3. (我认为这是最大的收获)因为你似乎总是取第一个结果 -FirstOrDefault()在 LINQ 中直接选择 - 不要让它在找到匹配项时枚举整个事情
ConcurrentDictionary<double, double> compareDictionary = new ConcurrentDictionary<double, double>();

foreach (var item in table1)
    {
        var sample = (string)item["ItemName"]; // cache the value before looping through inner collection
        var fooItem = table2.AsEnumerable()
                            .FirstOrDefault(foo => ((string)foo["FooName"]).Contains(sample)); // you seem to always take First item, so you could instruct LINQ to stop after a match is found
        if (fooItem != null && (double)fooItem["FooId"] != 0)
        {
            compareDictionary.TryAdd((double)item["ItemId"], (double)fooItem["FooId"]);
        }
    }

看来,将.FirstOrDefault()条件应用于 LINQ 查询语法无论如何都会将其简化为方法链语法,所以我会一直选择方法链接,并留给你来弄清楚美学


推荐阅读