我们一直在Linq模式下查询DataTable
中的数据,并且从来没有遇到过任何性能问题。在接下来的案例中,Linq使用600s+,但使用DataTable.Select()
只需要3秒。结果是一致的。直觉告诉我,Linq不应该那么慢,我的操作有问题,但我不知道如何改进,有人能给我一些建议吗?
GetDtTest_Base()
和GetDtTest_Info()
是通过ADO.NET中的SqlDataAdapter
从Sql Server返回的DataTable
和DataSet
。
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;
}
}
发布于 2020-06-11 14:37:21
另一种方法是创建一个为查找而设计的结构。
最初的方法将为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();
}
发布于 2021-04-13 09:27:41
有点严肃的挖掘,但最近在旧代码中遇到了DataTable.AsEnumerable()...
的性能问题,这些问题通过转换到DataTable.AsEnumerable().ToList()...
得到了解决。
Fabio的答案是+1,这绝对是一种更简洁、更高效的方法,但是如果你不想过多地处理遗留代码,并且分析会在你的linq查询中显示一个热路径,那么尝试将其转换为列表,看看效果如何。
https://stackoverflow.com/questions/62317744
复制相似问题