【发布时间】:2013-01-01 13:54:51
【问题描述】:
我被要求生成一份报告,该报告由针对 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 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 无法有效处理,还是我错过了一个技巧?
【问题讨论】:
-
我猜是所有的 FirstOrDefaults 造成的。您是否尝试在 groupby 之前添加
let response = r.First()?或者交换 Select 和 OrderBy?像这样stackoverflow.com/a/5013740/736079 -
是否有类似
User.Responses的导航属性? -
@jessehouwing 使用 let 响应有很大帮助,尽管 LINQ 版本仍然比 ADO.NET 慢得多。如果你输入这个作为答案,我至少会赞成它。我对 Jon Skeet 交换 select 和 order by 的策略有问题,主要是我不知道如何用这个结构来计算。
-
我也很难理解查询。如果您将纯 SQL 分享给我们,可能会有所帮助。
-
@GertArnold 是的,我确实确认添加导航属性也有 3 倍的帮助。
标签: c# linq entity-framework optimization linq-to-entities