【问题标题】:Efficient LINQ Query for large dataset大型数据集的高效 LINQ 查询
【发布时间】:2013-02-07 10:46:25
【问题描述】:

我有一个在包含 5,00,000 多条记录的数据表上运行的 LINQ 查询。此查询仅返回一行,但运行几乎需要 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。 有没有什么有效的方法来查询这么大的数据集

编辑

我尝试逐步调试程序,发现这两个查询实际上运行得很快,并且在我将此查询的结果集添加到 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 的结果集计数 = 1

【问题讨论】:

  • " 有没有什么有效的方法可以查询这么大的数据集" 是的,使用数据库!
  • 您是否已分解查询以查看最慢的部分?例如。您可以在您的第一个Select 之后停下来并致电ToList()。然后添加Where 并调用ToList() 等。这将帮助您识别导致最慢的部分。然而,正如 Tim 所说,这应该在数据库中完成。
  • 我怀疑您可能会感到困惑。一旦你开始迭代集合,它就会执行你的查询。 callDetailsForNodes 只是一个查询,直到您导致它执行为止。将 1 项添加到 ObservableCollection 应该不会很慢。

标签: c# performance linq


【解决方案1】:

在调用之前将 dtMin、dtMax 和 dtMinTime 转换为数据单位 (dr.time) 会有所帮助。然后,您可以摆脱在每条记录上多次发生的 Convert.ToDateTime。

【讨论】:

  • 我应该在哪里做,在数据表本身或在我在代码中获取它时,即 time = Convert.ToDateTime(dr["F3"])
  • @RajeevKumar 你在评论中写了什么。
  • @RajeevKumar 在您的第一个选择中,您设置了time = dr["F3"]。我认为 Rawling 的建议是将其更改为 time = Convert.ToDateTime(dr["F3"])。这样time 是一个日期时间,不必在Where 中多次转换。
  • @RajeevKumar,我的意思是你在哪里设置 dtMin 等。将它们的类型更改为与数据表时间相同的类型,然后直接比较。这样您就没有任何每行转换开销。
【解决方案2】:

我已经稍微整理了您的查询(尽管这不会对性能产生很大影响,并且可能存在拼写错误,因为我手头没有 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());

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-06
    • 2014-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多