如何将含ROW_NUMBER的SQL删除语句转换为EF Core 5 Lambda表达式
解决方案1:使用EF Core内置RowNumber窗口函数(性能最优,适配你原SQL逻辑)
EF Core 5 原生支持通过EF.Functions.RowNumber调用SQL的ROW_NUMBER窗口函数,和你原SQL的CTE逻辑完全对应:
// 替换为你实际的Col1过滤值 var targetColValue = "你要过滤的Col1参数值"; // 构造需要删除的记录查询 var deleteQuery = _unitOfWork.tables .Where(t => t.Col1 == targetColValue) .Select(t => new { t, RowNum = EF.Functions.RowNumber( ordering: EF.Property<DateTime>(t, nameof(Table.Col2)), // 这里把DateTime替换为Col2实际的类型 sortOrder: SortOrder.Descending) }) .Where(x => x.RowNum > 10) .Select(x => x.t); // 直接传入查询执行批量删除 await _unitOfWork.tables.DeleteAsync(deleteQuery);
注意:该方案需要你使用EF Core官方SqlServer数据库提供者,会自动生成和你示例几乎一致的CTE删除SQL,无需加载实体到内存,性能最高。
额外提醒:不要使用LINQ自带的Select((item, index) => ...)重载计算行号,该重载会将所有符合条件的记录加载到内存后再计算行号,数据量大时性能极低,一定要用EF.Functions.RowNumber生成数据库端执行的窗口函数。
解决方案2:兼容所有EF Core版本的通用写法(无窗口函数依赖)
如果你不方便使用窗口函数,也可以通过先查询要保留的前10条主键,再反向删除剩余记录的方式实现,逻辑更直观:
var targetColValue = "你要过滤的Col1参数值"; // 查出需要保留的前10条记录的主键(假设你的表主键为Id,自行替换为实际主键字段) var keepIds = await _unitOfWork.tables .Where(t => t.Col1 == targetColValue) .OrderByDescending(t => t.Col2) .Take(10) .Select(t => t.Id) .ToListAsync(); // 传入删除条件:匹配Col1且不在保留列表中 await _unitOfWork.tables.DeleteAsync(t => t.Col1 == targetColValue && !keepIds.Contains(t.Id));
注意:该方案需要将保留的主键加载到内存,适合单批次过滤后数据量不大的场景,如果数据量极大推荐使用方案1。
内容的提问来源于stack exchange,提问作者Glen
相关产品推荐
相关产品推荐

