LINQ群情激愤
本文关键字:LINQ | 更新日期: 2023-09-27 18:19:42
我有
var result = (from rev in Revisions
join usr in Users on rev.UserID equals usr.ID
join clc in ChangedLinesCounts on rev.Revision equals clc.Revision
select new {rev.Revision,
rev.Date, usr.UserName, usr.ID, clc.LinesCount}).Take(6);
我在不同的表上进行了几个连接,与这个问题无关,键是什么,但在这个查询的末尾,我的result
"表"包含
{Revision, Date, UserName, ID, LinesCount}
现在我执行e GroupBy
来计算每个用户的总行数。
所以。。
from row in result group row by row.ID into g {1}
select new {
g.Key,
totalCount = g.Sum(count=>count.LinesCount)
};
所以我得到了一个Key=ID
和totalCount=Sum
,但是
困惑
我也希望其他领域的最终结果。在我的理解中,{1}
分组查询后的"表"由组成
{Revision, Date, UserName, ID, LinesCount, TotalCount}
如果我的假设是正确的,为什么我不能做这样的事情:
from row in result group row by row.ID into g {1}
select new {
g.Key,
g.Revision //Revision doesn't exist ! Why ??
totalCount = g.Sum(count=>count.LinesCount)
};
但
from row in result group row by row.ID into g {1}
select new {
g.Key,
Revision = g.Select(x=>x.Revision), //Works !
totalCount = g.Sum(count=>count.LinesCount)
};
工作!,但是imo太糟糕了,因为我执行了另一个Select
。
通过查看LinqPad
SQL输出,我得到2SQL查询。
问题
有什么优雅和最佳的方法可以做到这一点吗?或者我总是需要运行Select
在分组数据上,为了能够访问字段,是否存在?
问题是,您只能根据ID进行分组-如果您在SQL中这样做,您也无法访问其他字段。。。
要获得其他字段,您必须将它们包含在您的group子句中:
from row in result group row by new { row.ID, row.Revision } into g
select new {
g.Key.ID,
g.Key.Revision
totalCount = g.Sum(count=>count.LinesCount)
};
这里的问题是您的输出在逻辑上看起来像这样:
Key = 1
Id = 1, Revision = 3587, UserName = Bob, LinesCount = 34, TotalCount = 45
Id = 1, Revision = 3588, UserName = Joe, LinesCount = 64, TotalCount = 54
Id = 1, Revision = 3589, UserName = Jim, LinesCount = 37, TotalCount = 26
Key = 2
Id = 2, Revision = 3587, UserName = Bob, LinesCount = 34, TotalCount = 45
Id = 2, Revision = 3588, UserName = Joe, LinesCount = 64, TotalCount = 54
Id = 2, Revision = 3589, UserName = Jim, LinesCount = 37, TotalCount = 26
就像执行SQL GROUP BY
一样,一个值要么是键的一部分,因此每个组都是唯一的,要么是在细节中,因此会重复多次,并且每行可能不同。
现在,从逻辑上讲,Revision和UserName可能对每个Id都是唯一的,但Linq无法知道这一点(就像SQL无法知道一样)。
要解决这个问题,你需要了解如何指定你想要的修订版。例如:
Revision = g.FirstOrDefault(x => x.Revision)
为了避免多SQL问题,您需要使用一个可以转换为SQL的聚合函数,因为大多数SQL方言都没有first
运算符(结果集被认为是无序的,因此从技术上讲,没有项目是"第一")。
Revision = g.Min(x => x.Revision)
Revision = g.Max(x => x.Revision)
不幸的是,Linq没有字符串的min/max运算符,所以尽管SQL可能支持这一点,但Linq不支持。
在这种情况下,您可以为Id和总数生成一个中间结果集,然后将其连接回原始集以获取详细信息,例如:
from d in items
join t in (
from t in items
group by t.Id into g
select new { Id = g.Key, Total = g.Sum(x => x.LineCount) }
) on d.Id equals t.Id
select new { Id = d.Id, Revision = d.Revision, Total = t.Total }
Revision在您的第二个示例中不存在,因为它不是IGrouping<T>
的成员,在IGrouping<T>
中,您有一个Key
属性,它也是分组在一起的所有行的IEnumerable<T>
。因此,这些行中的每一行都具有Revision
,但对于分组本身没有Revision
。
如果具有相同ID的所有行的Revision
都是相同的,则可以使用FirstOrDefault()
,以便选择网络最多有一个答案:
from row in result group row by row.ID into g {1}
select new {
g.Key,
Revision = g.Select(x=>x.Revision).FirstOrDefault(),
totalCount = g.Sum(count=>count.LinesCount)
};
但是,如果Revision
不是每个ID
唯一的,则您希望使用@Tobias建议的匿名类型进行分组,然后您将获得基于ID
和Revision
的分组。