Linq中的Row_number over(由xxx分区)?

我有一个DataTable这个结构和数据:

 id |  inst | 名称
 ------------------------
  1 | 吉他| 约翰
  2 | 吉他| 乔治
  3 | 吉他| 保罗
  4 | 鼓| 林戈
  5 | 鼓| 皮特

我可以像这样检索logging:

IEnumerable <Beatle>... class Beatle { int id; string inst; string name; } 

我想得到那些演奏不同乐器的人的内在秩序。 在MSSQL我会用

 SELECT * ,Row_Number() OVER (PARTITION BY inst ORDER BY id) AS rn FROM Beatles 

这个查询返回

 id |  inst | 名字|  RN
 -----------------------------
  1 | 吉他| 约翰|  1
  2 | 吉他| 乔治|  2
  3 | 吉他|  paul |  3
  4 | 鼓|  ringo |  1
  5 | 鼓|  pete |  2

我怎么能在Linq做到这一点?

编辑(在接受答案后)

完整的工作代码:

 var beatles = (new[] { new { id=1 , inst = "guitar" , name="john" }, new { id=2 , inst = "guitar" , name="george" }, new { id=3 , inst = "guitar" , name="paul" }, new { id=4 , inst = "drums" , name="ringo" }, new { id=5 , inst = "drums" , name="pete" } }); var o = beatles.OrderBy(x => x.id).GroupBy(x => x.inst) .Select(g => new { g, count = g.Count() }) .SelectMany(t => tgSelect(b => b).Zip(Enumerable.Range(1, t.count), (j, i) => new { j.inst, j.name, rn = i })); foreach (var i in o) { Console.WriteLine("{0} {1} {2}", i.inst, i.name, i.rn); } 

Linq对象的另一个解决scheme是:

 var result = beatles .GroupBy(g => g.inst) // PARTITION BY ^^^^ .Select(c => c.OrderBy(o => o.id).Select((v, i) => new { i, v }).ToList()) // ORDER BY ^^ .SelectMany(c => c) .Select(c => new { cvid, cvinst, cvname, rn = ci + 1 }) .ToList(); 

[ C# Demo ]

试试这个class轮:

 var o = beatles .OrderBy( x => x.id ) .GroupBy( x => x.inst ) .Select( group => new { Group = group, Count = group.Count() } ) .SelectMany( groupWithCount => groupWithCount.Group.Select( b => b) .Zip( Enumerable.Range( 1, groupWithCount.Count ), ( j, i ) => new { j.inst, j.name, RowNumber = i } ) ); foreach (var i in o) { Console.WriteLine( "{0} {1} {2}", i.inst, i.name, i.RowNumber ); } 

输出:

 Guitar John 1 Guitar George 2 Guitar Paul 3 drums Ringo 1 drums Pete 2 

如果可能,另一个想法是使用视图。

正如@The_Smallest指出的那样,行号不被LINQ支持。 不过,您可以通过以下方式获取所需信息:

 var grouped = beatles.OrderBy( x => x.id ) .ToList() // required because SelectMany below doesn't evaluate to SQL .GroupBy( x => x.inst ); var rns = grouped.ToDictionary( x => x.Key, x => 1 ); var result = grouped .SelectMany( x => x.Select( y => new { inst = y.inst, name = y.name, rn = rns[y.inst]++ } ) ); 

有些人可能会发现在你的代码中使用正确的索引是有用的

。((item,i)=> new {Item = item,Index = i})

另一个解决scheme做相当于RANK()OVER(PARTITION BY“partitionBy”ORDER BY“orderBy”DESC):

  DataTable Rank(DataTable dt, string partitionBy, string orderBy, int whichRank) { DataView dv = new DataView(dt); dv.Sort = partitionBy + ", " + orderBy + " DESC"; DataTable rankDt = dv.ToTable(); rankDt.Columns.Add("Rank"); int rank = 1; for (int i = 0; i < rankDt.Rows.Count - 1; i++) { rankDt.Rows[i]["Rank"] = rank; DataRow thisRow = rankDt.Rows[i]; DataRow nextRow = rankDt.Rows[i + 1]; if (thisRow[partitionBy].ToString() != nextRow[partitionBy].ToString()) rank = 1; else rank++; } DataView selectRankdv = new DataView(rankDt); selectRankdv.RowFilter = "rank = " + whichRank; return selectRankdv.ToTable(); } 

B“H

我知道这是旧的。 但为什么解决scheme不简单呢?

 var o = beatles.GroupBy(x => x.inst) .SelectMany(g => g.Select((j, i) => new { j.inst, j.name, rn = i + 1 }) ); 

Context.ExecuteQuery<T>(string sQuery, object[] oParam)

这在大多数情况下更简单快捷。