如何为每个键进入 LINQ 一行
Posted
技术标签:
【中文标题】如何为每个键进入 LINQ 一行【英文标题】:How to get in LINQ one row for each key 【发布时间】:2018-07-30 12:26:29 【问题描述】:我有以下清单:
EMP_ID | UPDATED_DATE |标记 ------ | ------------ | ---- 111 | 2015 年 1 月 1 日 | 99 111 | 2013 年 1 月 1 日 | 85 111 | 2017 年 1 月 1 日 | 80 222 | 2011 年 1 月 1 日 | 70 222 | 2015 年 1 月 1 日 | 55 222 | 2002 年 1 月 1 日 | 60我必须为每个 ID 选择一行,最新的 UPDATED_DATE, 在我们的等:
EMP_ID | UPDATED_DATE |标记 ------ | ------------ | ---- 111 | 2017 年 1 月 1 日 | 80 222 | 2015 年 1 月 1 日 | 55这是订单代码:
empMarksList.OrderBy(x=>x.EMP_ID).ThenBy(y=>y.UPDATED_DATE)
【问题讨论】:
empMarksList.GroupBy(x => x.Id).Select(x=>x.OrderByDescending(y=>y.UPDATED_DATE).First())
?
OrderBy(x=>x.Id)
Id
来自哪里?
运气不好,你的答案在哪里??这是一个很好的答案!
Select values with max date for each ID的可能重复
你在这里使用EntityFramework吗?我可以在标签中看到它。
【参考方案1】:
使用GroupBy
:
var items = empMarksList
.GroupBy(e => e.EMP_ID)
.Select(grp => grp.OrderByDescending(v => v.UPDATED_DATE).First());
或者如果你想要一个字典:
var dict = empMarksList
.GroupBy(e => e.EMP_ID)
.ToDictionary(grp => grp.Key,
grp => grp.OrderByDescending(v => v.UPDATED_DATE).First());
【讨论】:
@arekzyla - 在这段代码中.First()
非常好,因为.GroupBy
总是保证至少一个结果。
@Enigmativity 我得到一个例外:'方法'First'只能用作最终查询操作。考虑在这种情况下使用“FirstOrDefault”方法。对于 Linq-To-Entities,它不起作用。此外,GROUP BY
不会从此查询中生成,而只会生成 OUTER APPLY
和 WHERE inner.Id = outer.Id
之类的东西。
@arekzyla 查询应该无关紧要,Here is a working example implementation。
@AmirPopovich 我不是 100% 确定,但检查 Max 应该比 OrderByDescending 更快,不是吗?例如。 grp.First(v => v.UPDATED_DATE == grp.Max(g => g.UPDATED_DATE))
@Marie 但是您的示例使用 Linq-to-Objects 而 OP 使用 EntityFramework(如您在标签中看到的那样),因此 Linq-To-Entities 使用提供程序,您可以仅使用First
作为最终查询操作。【参考方案2】:
我更喜欢这个变体,但这和阿米尔的回答是一样的:
var query =
empMarksList
.GroupBy(x => x.EMP_ID)
.SelectMany(x => x.OrderByDescending(y => y.UPDATED_DATE).Take(1));
【讨论】:
首选,因为简单地取 1 而不是执行完整的结果然后返回第一个(第一个)项目。 @ItiTyagi -.First()
和 .Take(1)
都只返回第一项 - 都不执行完整的结果。 .Take(1)
更好,因为如果它之前的查询返回一个空列表,那么它不会抛出。
“因为如果之前的查询返回”是什么意思?
如果您担心一个空列表,您可以使用FirstOrDefault
,它具有相同的结果,但在语义上更正确。在这种情况下,您的枚举由 GroupBy 返回,我 99% 确信 GroupBy 不能返回空列表。这没有任何意义。
@ItiTyagi - 我说“如果它之前的查询返回一个空列表”只是作为一个假设的评论。在这种情况下它不能,但是如果插入了.Where
,那么它是可能的,那么最好养成一个好习惯。然后.FirstOrDefault
将提供不同的语义。【参考方案3】:
另一种选择是:
var items = context.EmpMarks
.GroupBy(e => e.EMP_ID, (k, g) => g
.FirstOrDefault(e => g.Max(v => v.UPDATED_DATE) == e.UPDATED_DATE));
实际上应该在 SQL 中生成GROUP BY
。
【讨论】:
【参考方案4】:你可以这样使用:
var result = empMarksList.GroupBy(x => x.Id)
.Select(g =>
g.Aggregate((a, x) => a == null || a.UPDATRED_DATE < x.UPDATRED_DATE ? x : a));
这比使用OrderBy
要麻烦一些,但是这样你就不会订购所有的子集合了,这有点矫枉过正,而且会占用更多的资源。
编辑: 在@arekzyla 的回答之后,我意识到我的选项也可以这样写:
var items = empMarksList.GroupBy(
x => x.Id,
(k, g) => g.Aggregate((a, x) => a == null || a.UPDATRED_DATE < x.UPDATRED_DATE ? x : a));
它的可读性较差,但在子集合上会有一个集合演练,而不是两个,这在大多数情况下可以忽略不计。
我不确定在什么情况下生成的 SQL 会更优化,因此可能值得检查。
【讨论】:
以上是关于如何为每个键进入 LINQ 一行的主要内容,如果未能解决你的问题,请参考以下文章
如何为以下 Scenerio 编写 linq 查询 [关闭]