有什么方法可以优化这个LINQ到实体查询吗?

本文关键字:LINQ 实体 查询 方法 什么 优化 | 更新日期: 2023-09-27 17:54:14

我被要求生成一个报告,该报告是由一个对SQL Server数据库的相当复杂的SQL查询驱动的。由于报告的网站已经使用实体框架4.1,我想我会尝试使用EF和LINQ:

编写查询。
var q = from r in ctx.Responses
                    .Where(x => ctx.Responses.Where(u => u.UserId == x.UserId).Count() >= VALID_RESPONSES)
                    .GroupBy(x => new { x.User.AwardCity, x.Category.Label, x.ResponseText })
         orderby r.FirstOrDefault().User.AwardCity, r.FirstOrDefault().Category.Label, r.Count() descending
         select new
         {
             City = r.FirstOrDefault().User.AwardCity,
             Category = r.FirstOrDefault().Category.Label,
             Response = r.FirstOrDefault().ResponseText,
             Votes = r.Count()
         };

这个查询统计投票,但只来自提交了一定数量的最低要求投票的用户。

从性能的角度来看,这种方法是一个彻底的灾难,所以我们切换到ADO。NET和查询运行得非常快。我确实使用SQL Profiler查看了LINQ生成的SQL,尽管它看起来像往常一样糟糕,但我没有看到任何关于如何优化LINQ语句以使其更高效的线索。

下面是直接的TSQL版本:

WITH ValidUsers(UserId)
AS
(
    SELECT UserId
    FROM Responses
    GROUP BY UserId
    HAVING COUNT(*) >= 103
)
SELECT d.AwardCity
    , c.Label
    , r.ResponseText
    , COUNT(*) AS Votes
FROM ValidUsers u
JOIN Responses r ON r.UserId = u.UserId
JOIN Categories c ON r.CategoryId = c.CategoryId
JOIN Demographics d ON r.UserId = d.Id
GROUP BY d.AwardCity, c.Label, r.ResponseText
ORDER BY d.AwardCity, s.SectionName, COUNT(*) DESC

我想知道的是:这个查询对EF和LINQ来说太复杂了,不能有效地处理,还是我错过了一个技巧?

有什么方法可以优化这个LINQ到实体查询吗?

使用let来减少r.First()的数量可能会提高性能。这可能还不够。

 var q = from r in ctx.Responses
                .Where()
                .GroupBy()
     let response = r.First()
     orderby response.User.AwardCity, response.Category.Label, r.Count() descending
     select new
     {
         City = response.User.AwardCity,
         Category = response.Category.Label,
         Response = response.ResponseText,
         Votes = r.Count()
     };

也许这个更改提高了性能,删除了where子句中嵌套的sql select

首先获得每个用户的投票并将其放入Dictionary

var userVotes = ctx.Responses.GroupBy(x => x.UserId )
                             .ToDictionary(a => a.Key.UserId,  b => b.Count());
var cityQuery = ctx.Responses.ToList().Where(x => userVotes[x.UserId] >= VALID_RESPONSES)
               .GroupBy(x => new { x.User.AwardCity, x.Category.Label, x.ResponseText })
               .Select(r => new
                       {
                           City = r.First().User.AwardCity,
                           Category = r.First().Category.Label,
                           Response = r.First().ResponseText,
                           Votes = r.Count()
                       })
               .OrderByDescending(r => r.City, r.Category, r.Votes());