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

将SQL ROW_NUMBER转换为LINQ时出现错误翻译及性能问题

问题:将目标SQL转换为Entity Framework Core 8的LINQ查询

需要转换的SQL逻辑是:筛选出最近1小时内、Location为USA的CountryStaff记录,按StaffId和Location分组后,取每组中最新的一条记录(按Timestamp倒序排序后的第一条)。原SQL代码如下:

SELECT * 
FROM(
    SELECT ROW_NUMBER() OVER (PARTITION BY StaffId,[Location] ORDER BY [Timestamp] DESC) AS rn, 
           StaffId, 
           Location, 
           [Timestamp] 
    FROM CountryStaff 
    WHERE [Timestamp] >= 
            DATEADD(hour,-1, GETDATE())
            AND [Location] = 'USA'
) x  
WHERE x.rn = 1

尝试的LINQ代码及问题

第一段代码(返回错误结果)

编写的LINQ代码:

var date = DateTime.UtcNow.AddHours(-1);
var filteredTransactions = CountryStaff
    .Where(t => t.Location == "USA" && t.Timestamp >= date)
    .OrderByDescending(t => t.Timestamp);

filteredTransactions
    .OrderByDescending(t => t.Timestamp)
    .Skip(0)
    .Take(1)
    .Select(t => new
    {
        StaffId = t.StaffId,
        Location = t.Location,
        Timestamp = t.Timestamp
    }).ToList();

这段代码生成的SQL未按StaffId和Location分区,仅返回整个筛选结果的第一条记录,不符合需求。生成的SQL如下:

DECLARE @p0 NVarChar(1000) = 'USA'
DECLARE @p1 DateTime = '2024-02-28 06:25:22.590'
DECLARE @p2 Int = 0
DECLARE @p3 Int = 1

SELECT [t2].[StaffId], [t2].[Location], [t2].[Timestamp]
FROM (
    SELECT [t1].[StaffId], [t1].[Timestamp], [t1].[Location], [t1].[ROW_NUMBER]
    FROM (
        SELECT ROW_NUMBER() OVER (ORDER BY [t0].[Timestamp] DESC, [t0].[Timestamp] DESC) AS [ROW_NUMBER], [t0].[StaffId], [t0].[Timestamp], [t0].[Location]
        FROM [CountryStaff] AS [t0]
        WHERE ([t0].[Location] = @p0) AND ([t0].[Timestamp] >= @p1)
    ) AS [t1]
    WHERE [t1].[ROW_NUMBER] BETWEEN @p2 + 1 AND @p2 + @p3
) AS [t2]
ORDER BY [t2].[ROW_NUMBER]

第二段代码(性能极差)

尝试以下代码后,结果符合要求,但执行耗时从原SQL的0.6ms飙升至5s:

CountryStaff 
    .Where(t => t.Timestamp >= date && t.Location == "USA")
    .GroupBy(t => new { t.StaffId, t.Location })
    .Select(g => g.OrderByDescending(t => t.Timestamp).First())
    .ToList();

正确的LINQ写法(匹配原SQL逻辑且性能优异)

在Entity Framework Core 8中,可直接使用RowNumber窗口函数实现原SQL的分区逻辑,生成的SQL与原SQL结构几乎一致,能保证原有的高性能:

var date = DateTime.UtcNow.AddHours(-1);

var result = CountryStaff
    .Where(t => t.Location == "USA" && t.Timestamp >= date)
    .Select(t => new 
    {
        t.StaffId,
        t.Location,
        t.Timestamp,
        Rn = EF.Functions.RowNumber().Over(
            partitionBy: new { t.StaffId, t.Location },
            orderBy: t => t.Timestamp descending)
    })
    .Where(x => x.Rn == 1)
    .Select(x => new 
    {
        x.StaffId,
        x.Location,
        x.Timestamp
    })
    .ToList();

该写法完全复刻原SQL的窗口函数分区逻辑,执行性能与原SQL一致。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 21:04:59