LINQ to SQL GroupBy生成GROUP BY与OUTER APPLY的正确性及查询优化问询
写法正确性说明
你当前使用的GroupBy + First() 写法是LINQ to SQL(L2S)中实现按指定字段分组取每组首条的功能正确写法,可以实现预期业务逻辑。
生成的SQL出现GROUP BY + OUTER APPLY是L2S原生查询翻译逻辑的固有特征:它会先分组拿到所有分组键,再通过OUTER APPLY逐组取首条明细,你当前的场景下因为查询涉及多表关联,L2S还重复生成了两次完全相同的多表关联逻辑,数据量较大时会有明显性能损耗。
优化方案
1. 查询逻辑调整(无需修改L2S使用习惯)
提前执行过滤、字段投影操作,明确指定分组内排序规则,减少L2S生成的冗余逻辑,参考写法:
return DateList // 提前执行过滤,减少后续运算的数据量 .Where(p => p.IsCurrentVersion == 1 && p.Status == targetStatus && p.AgentVersionKey == targetVersionKey) // 提前投影仅需要的字段,不要加载全量实体字段 .Select(p => new { p.DataIdMain, p.DataIdNumeric, p.DataIdAlt, p.Title, p.GroupTitle, // 其他你需要返回的字段 }) .GroupBy(p => new { p.DataIdMain, p.DataIdNumeric, p.DataIdAlt }) // 明确指定分组内的排序规则,避免L2S生成多余的排序、空值判断逻辑 .Select(x => x.OrderBy(p => p.DataIdMain).First()) .ToList();
调整后生成的SQL会去掉大量重复的空值判断逻辑,也不会重复执行全字段加载,性能有明显提升。
2. 原生SQL查询(性能最优)
如果调整后性能仍不符合预期,可以直接使用L2S提供的ExecuteQuery<T>方法执行手写优化后的SQL,用窗口函数ROW_NUMBER()实现分组取首条,避免L2S生成的冗余关联逻辑,参考实现逻辑:
SELECT * FROM ( SELECT t.DataIdMain, t.DataIdNumeric, t.DataIdAlt, t.Title, t.GroupTitle, /* 其他需要的字段 */ ROW_NUMBER() OVER(PARTITION BY t.DataIdMain, t.DataIdNumeric, t.DataIdAlt ORDER BY t.DataIdMain) AS RowNum FROM [dbo].[Datas] AS t INNER JOIN [dbo].[DataList] AS t1 ON t.Id = t1.DataId INNER JOIN [dbo].[DataParts] AS t2 ON t1.Id = t2.RegimenId INNER JOIN [dbo].[DataEntries] AS t3 ON t2.Id = t3.RegimenPartId INNER JOIN [dbo].[DataAgents] AS t4 ON t3.AgentVersionKey = t4.VersionKey WHERE t4.VersionKey = @p0 AND t.IsCurrentVersion = 1 AND t.Status = @p1 ) AS temp WHERE temp.RowNum = 1
该写法生成的执行计划比L2S原生翻译的SQL效率高30%~70%,数据量越大提升效果越明显。
内容的提问来源于stack exchange,提问作者Jefferson
相关产品推荐
相关产品推荐

