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

将多分组+按年周筛选的T-SQL查询转换为LINQ方法语法

将T-SQL查询转换为LINQ方法语法并解决EF周筛选问题

原T-SQL查询

select AppName, MethodName, sum(ElapsedMilliseconds)/count(MethodName) as avgMS
from [dbo].[AppMethodPerformance]
where YEAR(MethodDateTime) = 2023 and DatePart(week, MethodDateTime) = 4
group by appname, MethodName
order by AppName, MethodName

问题描述

需要将上述T-SQL转换为LINQ方法语法,尝试两种写法均未成功;同时自定义的ISOWeek方法无法在EF中使用(无法转换为SQL),需找到按年周筛选的替代方案。

用户尝试的代码:

List<AppMethodPerformance> allEntires = new();

using (var ctx = new AppLogContext(clientName))
{
    // 第一种尝试(已注释)
    //allEntires = await ctx.AppMethodPerformances
    //                        .Select(s => new { s.ElapsedMilliseconds, s.AppName, s.MethodName, s.MethodDateTime})
    //                        .Where(s => s.MethodDateTime.Year == year && ISOWeek(s.MethodDateTime) == week)
    //                        .GroupBy(s => new { s.AppName, s.MethodName, s.ElapsedMilliseconds } ).Sum(x => x.Key.ElapsedMilliseconds)
    //                        .Select(s => 
    //                                new AppMethodPerformance
    //                                {
    //                                    AppName = s.Key.AppName,
    //                                    MethodName = s.Key.MethodName,
    //                                    ElapsedMilliseconds = s.Key.ElapsedMilliseconds
    //                                })
    //                        .OrderBy(s => s.AppName).ThenBy(s => s.MethodName)
    //                        .ToListAsync();
    // 第二种尝试
    allEntires = await ctx.AppMethodPerformances
                            .Where(s => s.MethodDateTime.Year == year && ISOWeek(s.MethodDateTime) == week)
                            .GroupBy(s => new
                            {
                                s.AppName,
                                s.MethodName,
                                s.ElapsedMilliseconds
                            })
                            .Select(s => 
                                    new AppMethodPerformance 
                                    { 
                                        AppName = s.Key.AppName,
                                        MethodName = s.Key.MethodName,
                                        ElapsedMilliseconds = s.Key.ElapsedMilliseconds
                                    })
                            .ToListAsync();
}

参考的自定义ISOWeek方法:

private static int ISOWeek(DateTime methodDateTime)
{
    CultureInfo myCI = new CultureInfo("en-US");
    Calendar myCal = myCI.Calendar;

    // 获取GetWeekOfYear所需的DTFI属性
    CalendarWeekRule myCWR = myCI.DateTimeFormat.CalendarWeekRule;
    DayOfWeek myFirstDOW = myCI.DateTimeFormat.FirstDayOfWeek;

    return myCal.GetWeekOfYear(DateTime.Now, myCWR, myFirstDOW);
}

解决方案

一、正确的LINQ方法语法

核心错误在于分组键包含了ElapsedMilliseconds,导致无法按应用名和方法名聚合计算平均值。正确写法如下:

using (var ctx = new AppLogContext(clientName))
{
    var query = ctx.AppMethodPerformances
        .Where(s => s.MethodDateTime.Year == year && /* 周筛选条件,见下文 */)
        .GroupBy(s => new { s.AppName, s.MethodName })
        .Select(g => new AppMethodPerformance
        {
            AppName = g.Key.AppName,
            MethodName = g.Key.MethodName,
            ElapsedMilliseconds = g.Sum(item => item.ElapsedMilliseconds) / g.Count()
        })
        .OrderBy(s => s.AppName)
        .ThenBy(s => s.MethodName);

    allEntires = await query.ToListAsync();
}

这段代码会生成与原T-SQL逻辑完全一致的SQL,实现按应用名和方法名分组、计算平均耗时并排序。

二、EF中按年周筛选的替代方案

自定义方法无法被EF转换为SQL,需使用EF支持的内置函数或日期区间筛选:

方案1:使用EF内置DatePart函数(匹配原T-SQL逻辑)

EF Core 3.0+可直接使用EF.Functions.DatePart映射到SQL的DATEPART函数,完美匹配原T-SQL的DatePart(week, ...):

.Where(s => s.MethodDateTime.Year == year && 
            EF.Functions.DatePart("week", s.MethodDateTime) == week)

若使用旧版EF(非Core),替换为DbFunctions.DatePart即可。

方案2:匹配自定义ISOWeek的美式周规则

你的自定义方法使用美式周规则(一周从周日开始,第一周包含1月1日),方案1的DatePart("week", ...)已经完全对应这个逻辑,无需额外调整。如果需要严格的ISO 8601周规则(一周从周一开始,第一周至少4天在当年),可改为:

.Where(s => s.MethodDateTime.Year == year && 
            EF.Functions.DatePart("iso_week", s.MethodDateTime) == week)

方案3:日期区间筛选(避免函数转换问题)

如果担心数据库函数兼容性,可预先计算目标周的起始和结束日期,直接按日期区间筛选:

// 计算美式周的起始和结束日期(一周从周日开始)
var firstDayOfYear = new DateTime(year, 1, 1);
int daysToFirstSunday = (int)DayOfWeek.Sunday - (int)firstDayOfYear.DayOfWeek;
if (daysToFirstSunday < 0) daysToFirstSunday += 7;
var firstSunday = firstDayOfYear.AddDays(daysToFirstSunday);
var targetWeekStart = firstSunday.AddDays((week - 1) * 7);
var targetWeekEnd = targetWeekStart.AddDays(6);

// 按日期区间筛选
.Where(s => s.MethodDateTime >= targetWeekStart && s.MethodDateTime <= targetWeekEnd)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 09:15:35