重构GroupBy以避免减慢对大数据集的操作

时间:2015-06-14 17:04:55

标签: c# performance linq

我有一个大集合,我需要根据两个属性获取最新项目。 第一步是根据日期道具排序列表。这一切都很好而且很快。

然后我按两个属性对新列表进行分组,并从每个属性中取出第一个项目。

var one = Fisks.Where(s=>s.Havn.Id == 1).OrderByDescending(s=>s.Date);
var two = one.GroupBy(s=>new {s.Arter.Name, s.Sort});
var three = two.Select(s=>s.FirstOrDefault());

这样可行,但在大型集合中使用它时效果确实很慢。如何避免使用groupBy但仍然得到相同的结果?

谢谢!

2 个答案:

答案 0 :(得分:0)

仅在第一步中使用LINQ,然后在循环中使用第一步,这样可以更好地控制过程并避免完全分组:

var query = Fisks
    .Where(f => f.Havn.Id == 1)
    .OrderByDescending(f => f.Date)
    .ThenBy(f => f.Arter.Name)
    .ThenBy(f => f.Sort);
var list = new List<Fisk>();
foreach (Fisk fisk in query) {
    if (list.Count == 0) {
        list.Add(fisk);
    } else {
        Fisk last = list[list.Count - 1];
        if (fisk.Sort != last.Sort || fisk.Arter.Name != last.Arter.Name) {
            list.Add(fisk);
        }
    }
}

答案 1 :(得分:0)

一般来说,在做一些可能会破坏该命令的事情之前,我建议不要进行排序(例如GroupBy可以在SQL中由LINQ2SQL生成)。另外,请尝试仅订购您要使用的东西。如果仅限制所选字段/属性,则可以提高查询性能。您可以使用此sample来改变它,并使用真正的后端:

var Fisks=new[]{
    new {Havn=new{Id=1},Date=DateTime.MinValue,Arter=new{Name="A"},Sort=1,Title="A1"},
    new {Havn=new{Id=1},Date=DateTime.MinValue.AddDays(1),Arter=new{Name="A"},Sort=1,Title="A2"},
    new {Havn=new{Id=1},Date=DateTime.MinValue,Arter=new{Name="B"},Sort=1,Title="B1",},
    new {Havn=new{Id=1},Date=DateTime.MinValue.AddDays(2),Arter=new{Name="B"},Sort=1,Title="B2",},
    new {Havn=new{Id=1},Date=DateTime.MinValue.AddDays(2),Arter=new{Name="B"},Sort=1,Title="B3",},
};
var stopwatch=Stopwatch.StartNew();
var one = Fisks.Where(s=>s.Havn.Id == 1).OrderByDescending(s=>s.Date);
var two = one.GroupBy(s=>new {s.Arter.Name, s.Sort});
var three = two.Select(s=>s.FirstOrDefault());
var answer=three.ToArray();
stopwatch.Stop();
stopwatch.ElapsedTicks.Dump("elapsed Ticks");
answer.Dump();

stopwatch.Restart();
answer=Fisks
.Where(f=>f.Havn.Id.Equals(1))
.GroupBy(s=>new {s.Arter.Name, s.Sort},(k,g)=>new{
    s=g.OrderByDescending(s=>s.Date).First()//TOP 1 -> quite fast
})
.Select(g=>g.s)
.OrderByDescending(s=>s.Date) // only fully order results
.ToArray();
stopwatch.Stop();
stopwatch.ElapsedTicks.Dump("elapsed Ticks");
answer.Dump();

如果您正在使用任何SQL Server,则应检查LINQPad中生成的SQL。您不希望以n+1 Query结尾。在Havn.IdFisks.Date上建立索引也可能会有所帮助。