如何在LINQ的GroupBy子句中选择最后一条记录

24

我有一个包含IDContactIdComment的简单表格。

输入图像描述

我想选择记录并按contactId进行分组。我使用了这个LINQ扩展方法声明:

Mains.GroupBy(l => l.ContactID)
 .Select(g => g.FirstOrDefault())
 .ToList()

它返回记录14。我该如何使用LINQ获取具有最高IDContactID?(即返回36

5个回答

47
你可以订购你的物品。
Mains.GroupBy(l => l.ContactID)
.Select(g=>g.OrderByDescending(c=>c.ID).FirstOrDefault()) 
.ToList()

1
尽管此查询将返回正确的数据,但它会编译成K+1个SQL语句,其中K是键的数量(使用LINQpad查看)。 我今天遇到了同样的问题,并以以下方式解决:var lastCommentIDs = Mains.GroupBy(k => k.ContactID, e => e.ID, (k, e) => new { k, lastID = e.Max(x => x)}); var result = lastCommentIDs.Join(Mains, o => o.lastID, i => i.ID, (o, i) => new { ContactID = o.k, LastComment = i.Comment).ToList(); - Dmytro
嗨,@Dmitrij。感谢您的反馈。Linqpad正在使用Linq to SQL,您尝试过使用Linq to entities吗? - tym32167
嗨,@tym32167。是的,我尝试过使用EF,不确定到底出了什么问题,但是EF从LINQ语句中生成了最丑陋和绝对低效的SQL查询。可能我做错了什么,不确定,但事实是:与EF相比,LINQ2SQL产生更可预测的查询(至少我知道在使用LINQ2SQL执行特定查询时会发生什么)。 - Dmytro

8

在组中使用OrderByDescending对项进行排序:

Mains.GroupBy(l => l.ContactID)
    .Select(g => g.OrderByDescending(l => l.ID).First())
    .ToList();

此外,在从组中选择项目时,不需要使用FirstOrDefault; 组始终至少有一个项目,因此您可以安全地使用First()

1
一个更短的版本:g => g.OrderBy(l => l.ID).Last() :) - Mr. Blond

6

也许使用Max而不是OrderByDescending可以提高性能(我不确定内部如何实现,需要进行测试):

var grouped = Mains.GroupBy(l => l.ContactID);
var ids = grouped.Select(g => g.Max(x => x.Id));
var result = grouped.Where(g => ids.Contains(g.Id));

我猜想这可能会导致一个查询,它将使用MAX,然后执行SELECT * FROM ... WHERE id IN ({max ids here}),这可能比OrderByDescending快得多。

如果我说的不对,请随意纠正我。


1
比OrderBy更快 - Sha-Pai Li
那最后一行应该是...?var result = Mains.Where(g => ids.Contains(g.Id)); - James in Indy

3

OrderByDescending

Mains.GroupBy(l => l.ContactID)
.Select(g=>g.OrderByDescending(c=>c.ID).FirstOrDefault()) 
.ToList()

是您最佳的解决方案

它按照最高ID降序排列,从名称上就很明显。


0

您可以使用MoreLinq来实现更简短的解决方案:

Main.OrderByDescending(i => i.Id).DistinctBy(l => l.ContactID).ToList();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接