You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C# LINQ实现SQL Server多列分区的Row_Number()窗口函数

如何用C# LINQ实现SQL Server中多列分区的Row_Number()窗口函数

表数据

locationidContractorIDResourceIDSTOTDTCostDateAFEID
15175703745048.2266.7896.442022-07-201093
15175703745035.4649.1170.922022-07-211093
15175703745054.6075.62109.202022-07-191093
15175703745053.9074.64107.802022-07-201093
15175713745025.5335.3651.062022-07-201093
15175713762570.9298.21141.842022-07-201093
15175713745087.93121.78175.862022-07-201093
15175713745051.0670.71102.122022-07-191093
15175703768060.9984.46121.982022-07-201093
15175703768053.9074.64107.802022-07-191093
15175703747853.9074.64107.802022-07-191093

目标SQL查询

SELECT  
    LocationID,
    AFEID,
    ContractorID,
    ResourceID,
    MAX(ST) AS MaxST,
    MAX(OT) AS MaxOT,
    MAX(DT) AS MaxDT,
    AVG(ST) AS AvgST,
    AVG(OT) AS AvgOT,
    AVG(DT) AS AvgDT,
    MIN(ST) AS MinST,
    MIN(OT) AS MinOT,
    MIN(DT) AS MinDT,
    CostDate,
    ROW_NUMBER() OVER (PARTITION BY afeid, contractorid, resourceid 
                       ORDER BY costdate DESC) AS rownum              
FROM 
    tbldata 
GROUP BY 
    LocationID, AFEID, ContractorID,
    ResourceID, CostDate

尝试过的LINQ查询(未成功)

tbldata.OrderByDescending(x => x.CostDate)
       .AsEnumerable()
       .GroupBy(x => new
       {
           x.LocationId,
           x.AfeId,
           x.ContractorId,
           x.ResourceId,
           x.CostDate,
       })
        .Select(grp => new
        {
            grp.Key.AfeId,
            grp.Key.ContractorId,
            grp.Key.ResourceId,
            grp.Key.LocationId,
            grp.Key.CostDate,
            MaxST = grp.Max(x => x.StandardTime),
            MaxOT = grp.Max(x => x.Overtime),
            MaxDT = grp.Max(x => x.DualTime),
            AvgST = grp.Average(x => x.StandardTime),
            AvgOT = grp.Average(x => x.Overtime),
            AvgDT = grp.Average(x => x.DualTime),
            MinST = grp.Min(x => x.StandardTime),
            MinOT = grp.Min(x => x.Overtime),
            MinDT = grp.Min(x => x.DualTime),
            count = grp.Count(),
            rownum = grp.Zip(Enumerable.Range(1, grp.Count()), (j, i) => new { rownum = i}).FirstOrDefault()
        });

期望输出

rownumLocationIDAFEIDContractorIDResourceIDMaxSTMaxOTMaxDTAvgSTAvgOTAvgDTMinSTMinOTMinDTCostDate
1151093175703745035.4649.1170.9235.46000049.1170.92035.4649.1170.922022-07-21
2151093175703745053.9074.64107.8051.06000070.71102.1248.2266.7896.442022-07-20
3151093175703745054.6075.62109.2054.60000075.62109.2054.6075.62109.202022-07-19
1151093175703747853.9074.64107.8053.90000074.64107.8053.9074.64107.802022-07-19
1151093175703768060.9984.46121.9860.99000084.46121.9860.9984.46121.982022-07-20
2151093175703768053.9074.64107.8053.90000074.64107.8053.9074.64107.802022-07-19
1151093175713745087.93121.78175.8656.73000078.57113.4625.5335.3651.062022-07-20
2151093175713745051.0670.71102.1251.06000070.71102.1251.0670.71102.122022-07-19
1151093175713762570.9298.21141.8470.92000098.21141.8470.9298.21141.842022-07-20

解决方案

你原来的LINQ逻辑顺序错误:SQL中是先分组得到每日聚合数据,再对这些聚合数据按AFEID、ContractorID、ResourceID分区,按CostDate降序生成行号。而你之前的代码是先排序,再分组,然后试图在每个日期分组内生成行号,这和SQL逻辑不符。

正确的LINQ实现需要分两步:

  1. 先按LocationID、AFEID、ContractorID、ResourceID、CostDate分组,计算聚合值,得到每个日期分组的统计结果。
  2. 对聚合结果按AFEID、ContractorID、ResourceID分区,在每个分区内按CostDate降序排序,然后生成行号。

完整的LINQ代码如下:

var result = tbldata
    // 第一步:对应SQL的GROUP BY,生成每日聚合数据
    .GroupBy(x => new
    {
        x.LocationId,
        x.AfeId,
        x.ContractorId,
        x.ResourceId,
        x.CostDate
    })
    .Select(grp => new
    {
        grp.Key.LocationId,
        grp.Key.AfeId,
        grp.Key.ContractorId,
        grp.Key.ResourceId,
        grp.Key.CostDate,
        MaxST = grp.Max(x => x.StandardTime),
        MaxOT = grp.Max(x => x.Overtime),
        MaxDT = grp.Max(x => x.DualTime),
        AvgST = grp.Average(x => x.StandardTime),
        AvgOT = grp.Average(x => x.Overtime),
        AvgDT = grp.Average(x => x.DualTime),
        MinST = grp.Min(x => x.StandardTime),
        MinOT = grp.Min(x => x.Overtime),
        MinDT = grp.Min(x => x.DualTime)
    })
    // 第二步:对应SQL的PARTITION BY,按AFEID、ContractorID、ResourceID分区
    .GroupBy(x => new { x.AfeId, x.ContractorId, x.ResourceId })
    // 在每个分区内按CostDate降序排序,生成行号
    .SelectMany(partition => 
        partition.OrderByDescending(item => item.CostDate)
                 .Select((item, index) => new
                 {
                     rownum = index + 1, // 索引从0开始,所以+1得到行号
                     item.LocationId,
                     item.AfeId,
                     item.ContractorId,
                     item.ResourceId,
                     item.MaxST,
                     item.MaxOT,
                     item.MaxDT,
                     item.AvgST,
                     item.AvgOT,
                     item.AvgDT,
                     item.MinST,
                     item.MinOT,
                     item.MinDT,
                     item.CostDate
                 })
    )
    .ToList();

这段代码完全对应你的SQL逻辑,执行后就能得到你期望的输出结果。

内容的提问来源于stack exchange,提问作者avnish maddheshiya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 15:24:23