针对大型数据集的高效LINQ查询

时间:2013-02-07 10:46:26

标签: c# performance linq

我有一个在数据表上运行的LINQ查询,它有5,00,000多个记录。此查询只返回一行,但需要大约30秒才能运行。这是我的查询

  var callDetailsForNodes = from records in dtRowForNode.Select().Select(dr =>
    new
    {
        caller1 = StringComparer.CurrentCultureIgnoreCase.Compare(dr["F1"], dr["F2"]) < 0 ? dr["F1"] : dr["F2"],
        caller2 = StringComparer.CurrentCultureIgnoreCase.Compare(dr["F1"], dr["F2"]) < 0 ? dr["F2"] : dr["F1"],
        time = dr["F3"],
        filters = dr.Field<string>("F9")
    }).Where(dr => (dtMin <= Convert.ToDateTime(dr.time)) && (dtMax >= Convert.ToDateTime(dr.time)) && (lstCallType.Contains(dr.filters))
             && (dtMinTime <= Convert.ToDateTime(dr.time).TimeOfDay) && (dtMaxTime >= Convert.ToDateTime(dr.time).TimeOfDay))
    .GroupBy(drg => new { drg.caller1, drg.caller2 })
    .Select(drg => new { drg.Key.caller1, drg.Key.caller2, count = drg.Count() }).AsEnumerable()
                                  where (records.caller1.ToString() == VerSelected || records.caller2.ToString() == VerSelected)
                                  select records;

我再次运行一个查询来重新排列数据,将其从上面的查询中获取

 var callDetailsForNodes_ReArrange = from records in callDetailsForNodes.Select(r => new
        {
            caller1 = r.caller1.ToString() == VerSelected ? r.caller1 : r.caller2,
            caller2 = r.caller1.ToString() != VerSelected ? r.caller1 : r.caller2,
            count = r.count
        })
        select records;

然后我只是将这个集合绑定到gridview。 有没有有效的方法来查询这么大的数据集

修改

我尝试逐步调试程序,发现这2个查询实际上运行得很快,并且在我将此查询的结果集添加到ObservableCollection以将其绑定到gridview的步骤中进行了操作。这是代码

foreach (var callDetailsForNode_ReArrange in callDetailsForNodes_ReArrange)
        {

                _CallForNodes.Add(new CallForNodeData
                {
                    Caller1 = callDetailsForNode_ReArrange.caller1.ToString(),
                    Caller2 = callDetailsForNode_ReArrange.caller2.ToString(),
                    Count = callDetailsForNode_ReArrange.count
                });

        }

这里callDetailsForNodes_ReArrange有结果集count = 1

2 个答案:

答案 0 :(得分:0)

有一件事可能有助于在调用之前将dtMin,dtMax和dtMinTime转换为数据单位(dr.time)。然后你就可以摆脱每条记录多次发生的Convert.ToDateTime。

答案 1 :(得分:0)

我已经稍微整理了你的查询(虽然这不会对性能产生很大的影响,并且可能存在拼写错误,因为我没有手持VS)。在您的编辑中,您似乎对LINQ中的延迟执行感到困惑。 callDetailsForNodes并不代表您的结果 - 它是一个在执行结果后会提供结果的查询。

如果您必须在进行所有这些查询,我建议您在第一次选择后添加ToList并单独运行。然后将ToList添加到Where子句中。调用ToList会强制执行查询,您将看到延迟的位置。

最后一点注意事项 - 您应该将记录直接传递给ObservableCollection构造函数,而不是为每个项目调用Add。调用Add将(我认为)导致集合引发更改的通知,这对于小型列表来说不是什么大问题,但会减慢更大列表的速度。

var callDetailsForNodes = dtRowForNode.AsEnumerable()
                              .Select(dr => new {
                                                    caller1 = StringComparer.CurrentCultureIgnoreCase.Compare(dr["F1"], dr["F2"]) < 0 ? dr["F1"] : dr["F2"],
                                                    caller2 = StringComparer.CurrentCultureIgnoreCase.Compare(dr["F1"], dr["F2"]) < 0 ? dr["F2"] : dr["F1"],
                                                    time = Convert.ToDateTime(dr["F3"]),
                                                    filters = dr.Field<string>("F9")})
                              .Where(dr => (dtMin <= dr.time) 
                                        && (dtMax >= dr.time) 
                                        && (lstCallType.Contains(dr.filters))
                                        && (dtMinTime <= dr.time.TimeOfDay) 
                                        && (dtMaxTime >= dr.time.TimeOfDay)
                                        && caller1 == VerSelected || caller2 == VerSelected))
                              .GroupBy(drg => new { drg.caller1, drg.caller2 })
                              .Select(drg => new { drg.Key.caller1, drg.Key.caller2, count = drg.Count());