我被要求生成一个由针对SQL Server数据库的相当复杂的SQL查询驱动的报告。由于报告的网站已经在使用Entity Framework 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事件探查器查看了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无法有效处理或者我错过了一招?
答案 0 :(得分:4)
使用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()
};
答案 1 :(得分:1)
也许这个更改提高了性能,删除了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());