繁体   English   中英

DataTable.AsEnumerable().Where() 与 DataTable.Select()

[英]DataTable.AsEnumerable().Where() vs DataTable.Select()

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

GetDtTest_Base()GetDtTest_Info()是Sql Server通过ADO.NET中的SqlDataAdapter返回的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;
    }
}

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

原始方法将为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();
}

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM