【问题标题】:how do I make this LINQ query faster?如何使这个 LINQ 查询更快?
【发布时间】:2015-11-21 23:14:48
【问题描述】:

modelData 在列表中有 100,000 个项目。

我在 2 个循环中执行 2 个“选择”。

它的结构是否可以不同 - 因为它需要很长时间 - 10 分钟

public class ModelData
{
    public string name;
    public DateTime DT;
    public int real;
    public int trade;
    public int position;
    public int dayPnl;
}

List<ModelData> modelData;

var dates = modelData.Select(x => x.DT.Date).Distinct();
var names = modelData.Select(x => x.name).Distinct();

foreach (var aDate in dates)
{
    var dateRealTrades = modelData.Select(x => x)
                                  .Where(x => x.DT.Date.Equals(aDate) && x.real.Equals(1));

    foreach (var aName in names)
    {
        var namesRealTrades = dateRealTrades.Select(x => x)
                                            .Where(x => x.name.Equals(aName));

        // DO MY PROCESSING
    }
}

【问题讨论】:

  • 您可以先删除.Select(x =&gt; x)
  • 我认为您只需要一个按日期分组然后按名称分组的查询。
  • 不必要或多余的.Distinct 可能会影响性能。这通常会强制对基础数据进行排序。确保没有其他方法可以完成同样的事情。
  • where 子句 x.real.Equals(1) 应该在 foreach 之前执行
  • 除了优化您的 Linq 查询之外,您绝对应该确保在相关数据库字段上有一个覆盖索引。这可能会使您的流程加快 >90%。 100k 行真的不是一个大数字。

标签: c# performance linq


【解决方案1】:

我相信使用 group by 的两个查询可以实现您想要的。一个按日期创建查找,另一个为您提供名称-日期分组项。

var data = modelData.Where(x => x.real.Equals(1))
                    .GroupBy(x => new { x.DT.Date, x.name });
var byDate = modelData.Where(x => x.real.Equals(1))
                      .ToLookup(x => x.DT.Date);

foreach(var item in data)
{
    var aDate = item.Key.Date; 
    var aName = item.Key.name;
    var namesRealTrades = item.ToList();
    var dateRealTrades = byDate[aDate].ToList();

    // DO MY PROCESSING
}

第一个查询将为您提供按名称和日期分组的项目以进行迭代,第二个查询将为您提供查找以获取与给定日期关联的所有项目。第二个使用查找,以便列表迭代一次,让您快速访问生成的项目列表。

这应该会大大减少您对 modelData 的迭代次数,而不是您当前拥有的次数。

【讨论】:

  • 谢谢。但是对于每个日期,我还需要最后一个“位置”和“dayPnl”。我如何调整您的代码以添加它们?
  • @ManInMoon 你是说你在// DO MY PROCESSING里面使用dateRealTrades
  • 我已更新为包含第二个查询,以便您按日期查找项目。
【解决方案2】:

你可以像这样重写你的 for 循环:

foreach (var namesRealTrades in names.Select(aName => dateRealTrades.Where(x => x.name.Equals(aName))))
{
   //DO STUFF
}

根据您的数据,这可能会减少您必须进行的查询次数

【讨论】:

  • 我不明白为什么这样做会更快 - 你能解释一下吗?
  • 根据您的数据,它将减少对数据库的查询调用次数。唯一确定的方法是尝试一下
【解决方案3】:

您是否尝试按照 MSDN 网站上的建议编译查询?

当您有一个执行结构相似的应用程序时 多次查询,通常可以通过编译来提高性能 查询一次并以不同的方式执行多次 参数。例如,应用程序可能必须检索所有 位于特定城市的客户,该城市在 用户在表单中运行时。 LINQ to SQL 支持使用 为此目的编译查询。

https://msdn.microsoft.com/en-us/library/bb399335(v=vs.110).aspx

【讨论】:

    【解决方案4】:

    有几点:

    • 使用 .ToList() 计算一次序列,以便以后保留。
    • 使用 .GroupBy() 避免重新搜索 modelData 以查找您已经找到的内容。

      // Collections of models having the same Date or Name.
      var dates = modelData.GroupBy(x => x.DT.Date);
      var names = modelData.GroupBy(x => x.Name);
      
      foreach (var modelsWithDate in dates)
      {
         var aDate = modelsWithDate.Key;
         var dateRealTrades = modelsWithDate.Where(x => x.real == 1).ToList();
      
         foreach (var modelsWithName in names)
         {
             var aName = modelsWithName.Key;
             var namesRealTrades = modelsWithName.ToList();
      
             // DO MY PROCESSING
         }
      }
      

    【讨论】:

    • 将 ToList() 添加到 var dates = modelData.GroupBy(x => x.DT.Date);下一行将确保它们在循环之前被拉入内存。
    • 如果你看问题,modelData已经是一个List。一切都在记忆中。
    【解决方案5】:

    代码无效有两种方式。

    • names 已延迟评估。每次您遍历它时,它都必须遍历整个数据才能再次找到所有不同的名称。您应该保存结果。
    • 您从集合中找到不同的值,然后您对每个不同的值再次进行收集并查看它的出现。您应该使用分组。

    重写后的代码可以是这样的

        var dates = modelData.GroupBy(x => x.DT.Date);
        var names = modelData.Select(x => x.name).Distinct().ToArray();
    
        foreach (var date in dates)
        {
            var dateRealTrades = date.Where(x => x.real.Equals(1)).ToArray();
            var namesRealTradesLookup = dateRealTrades.ToLookup(x => x.name);
    
            foreach (var aName in names)
            {
                var namesRealTrades = namesRealTradesLookup[aName];
    
                // DO MY PROCESSING
                // var aDate = date.Key;
            }
        }
    

    如果您对没有实际交易的日期/姓名组合不感兴趣,可以通过更直接的方式完成

        var realModelData = modelData.Where(x => x.real.Equals(1));
    
        foreach (var dateRealTrades in realModelData.ToLookup(x => x.DT.Date))
        {
            foreach (var namesRealTrades in dateRealTrades.ToLookup(x => x.name))
            {
    
                // DO MY PROCESSING
                //var aDate = dateRealTrades.Key;
                //var aName = namesRealTrades.Key;
                //foreach(var trade in namesRealTrades) { ...
                //foreach(var trade in dateRealTrades) { ...
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-02
      • 1970-01-01
      • 2022-01-13
      • 2014-11-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多