【问题标题】:DataTable.AsEnumerable().Where() vs DataTable.Select()DataTable.AsEnumerable().Where() 与 DataTable.Select()
【发布时间】:2020-06-11 05:35:34
【问题描述】:

我们一直在 Linq 模式下查询 DataTable 中的数据,并且从未遇到任何性能问题。直到以下情况,使用 Linq 需要 600s+,但使用 DataTable.Select() 只需要 3s。结果是一致的。直觉告诉我Linq不应该这么慢,而且我的操作有问题,但我不知道如何改进它,谁能给我一些建议?

GetDtTest_Base()GetDtTest_Info() 是在 ADO.NET 中通过 SqlDataAdapter 从 Sql Server 返回的 DataTableDataSet

DataTable dtBase = GetDtTest_Base();  //4W rows
DataSet dsInfo = GetDtTest_Info();    //two Datatable  4W rows, 2K rows

//Normally, we use Linq all the time. In this scenario, it takes about 600 seconds to query through Linq
foreach (DataRow item in dtBase.Rows)
{
    string pnum = item["pnum"].ToString();
    string number = item["number"].ToString();

    var query_Info1 = dsInfo.Tables[0].AsEnumerable()
        .Where(w => w.Field<string>("pnum") == pnum && w.Field<string>("calleee164") == number)
        .Select(s => s);
    item["conn"] = query_Info1.Count() > 0 ? (query_Info1.First())["conn"] : 0;
    item["total"] = query_Info1.Count() > 0 ? (query_Info1.First())["total"] : 0;

    var query_Info2 = dsInfo.Tables[1].AsEnumerable()
        .Where(w => w.Field<string>("pnum") == pnum && w.Field<string>("calleee164") == number)
        .Select(s => s);
    item["asCnt"] = query_Info2.Count() > 0 ? 1 : 0;
    item["asTrunks"] = query_Info2.Count() > 0 ? (query_Info2.First())["trunks"] : null;
}

//After changing this query, it only took 3 seconds to query
foreach (DataRow item in dtBase.Rows)
{
    string pnum = item["pnum"].ToString();
    string number = item["number"].ToString();

    DataRow[] query_Info1 = dsInfo.Tables[0].Select($"pnum='{pnum}' and calleee164='{number}'");
    if (query_Info1 != null && query_Info1.Length >= 1)
    {
        item["conn"] = query_Info1[0]["conn"].ToString();
        item["total"] = query_Info1[0]["total"].ToString();
    }
    else
    {
        item["conn"] = 0;
        item["total"] = 0;
    }

    DataRow[] query_Info2 = dsInfo.Tables[1].Select($"pnum='{pnum}' and calleee164='{number}'");
    if (query_Info2 != null && query_Info2.Length >= 1)
    {
        item["asCnt"] = 1;
        item["asTrunks"] = query_Info2[0]["trunks"].ToString();
    }
    else
    {
        item["asCnt"] = 0;
        item["asTrunks"] = null;
    }
}

【问题讨论】:

  • 尝试将dsInfo.Tables[0].AsEnumerable() 移到foreach 循环之外,因为这可能会产生开销。
  • var query_Info10 = dsInfo.Tables[0].AsEnumerable(); var query_Info20 = dsInfo.Tables[1].AsEnumerable(); foreach (DataRow item in dtBase.Rows) { ... ... var query_Info1 = query_Info10 .Where... ... } 问题依旧存在
  • 每次迭代查询两个数据表 4 次,Count 和 First,两次,而同样的事情可以在一次 FirstOrDefault() 调用中完成。

标签: c# performance linq datatable


【解决方案1】:

另一种方法是创建一个专为查找而设计的结构。

原始方法将为dtBase 中的每一行迭代dsInfo 表,即O(n*m)。使用查找数据结构从dsInfo 表中查找行将是一项操作,这使得 O(n)。

var base = GetDtTest_Base();
var infoSet = GetDtTest_Info();

var firstLookup = info[0].AsEnumerable()
    .ToLookup(row => (Num: row.Field<string>("pnum"), Callee: row.Field<string>("calleee164")));
var secondLookup = info[1].AsEnumerable()
    .ToLookup(row => (Num: row.Field<string>("pnum"), Callee: row.Field<string>("calleee164")));

foreach (DataRow item in dtBase.Rows)
{
    var pnum = item["pnum"].ToString();
    var number = item["number"].ToString();
    var key = (Num: pnum, Callee: number);

    item["conn"] = firstLookup[key].Select(row => row.Field<int>("conn")).FirstOrDefault();
    item["total"] = firstLookup[key].Select(row => row.Field<int>("total")).FirstOrDefault();

    item["asCnt"] = secondLookup[key].Any() ? 1 : 0;
    item["asTrunks"] = secondLookup[key].Select(row => row.Field<string>("trunks")).FirstOrDefault();
}

【讨论】:

  • 非常感谢,虽然我还是不太明白为什么上面写的很慢,查询后我用.ToList()还是没有效果,但是你写的真的很高效,非常感谢。
【解决方案2】:

有点严重,但最近在旧代码中遇到了DataTable.AsEnumerable()... 的性能问题,通过转换为DataTable.AsEnumerable().ToList()... 得到了解决。

+1 为 Fabio 的回答,绝对是一种更清洁、更有效的方法,但如果您不想过多使用遗留代码并且分析在您的 linq 查询中显示热路径,请尝试转换为列表并查看您的情况.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-17
    • 2010-11-11
    • 1970-01-01
    • 2011-03-09
    • 2011-04-26
    • 1970-01-01
    相关资源
    最近更新 更多