【问题标题】:Speed up LINQ query - EF5加速 LINQ 查询 - EF5
【发布时间】:2013-06-13 02:59:20
【问题描述】:

我有以下使用 EF5 和通用存储库的 LINQ 查询,SQL Server 2008 db 的工作单元模式

        var countriesArr = GetIdsFromDelimStr(countries);
        var competitionsArr = GetIdsFromDelimStr(competitions);
        var filterTeamName = string.Empty;

        if (teamName != null)
        {
            filterTeamName = teamName.ToUpper();
        }

        using (var unitOfWork = new FootballUnitOfWork(ConnFooty))
        {

            // give us our selection of teams
            var teams =
                (from team in
                     unitOfWork.TeamRepository.Find()
                 where ((string.IsNullOrEmpty(filterTeamName) || team.Name.ToUpper().Contains(filterTeamName)) &&
                        (countriesArr.Contains(team.Venue.Country.Id) || countriesArr.Count() == 0))
                 select new
                            {
                                tId = team.Id
                            }).Distinct();

            // give us our selection of contests
            var conts = (
                            from cont in
                                unitOfWork.ContestRepository.Find(
                                    c =>
                                    ((c.ContestType == ContestType.League && competitionsArr.Count() == 0) ||
                                     (competitionsArr.Contains(c.Competition.Id) && competitionsArr.Count() == 0)))
                            select new
                                       {
                                           contId = cont.Id
                                       }
                        ).Distinct();

            // get selection of home teams based on contest
            var homecomps = (from fixt in unitOfWork.FixtureDetailsRepository.Find()
                             where
                                 teams.Any(t => t.tId == fixt.HomeTeam.Id) &&
                                 conts.Any(c => c.contId == fixt.Contest.Id)
                             select new
                                        {
                                            teamId = fixt.HomeTeam.Id,
                                            teamName = fixt.HomeTeam.Name,
                                            countryId = fixt.HomeTeam.Venue.Country.Id != null ? fixt.HomeTeam.Venue.Country.Id : 0,
                                            countryName = fixt.HomeTeam.Venue.Country.Id != null ? fixt.HomeTeam.Venue.Country.Name : string.Empty,
                                            compId = fixt.Contest.Competition.Id,
                                            compDesc = fixt.Contest.Competition.Description
                                        }).Distinct();

            // get selection of away teams based on contest
            var awaycomps = (from fixt in unitOfWork.FixtureDetailsRepository.Find()
                             where
                                 teams.Any(t => t.tId == fixt.AwayTeam.Id) &&
                                 conts.Any(c => c.contId == fixt.Contest.Id)
                             select new
                             {
                                 teamId = fixt.AwayTeam.Id,
                                 teamName = fixt.AwayTeam.Name,
                                 countryId = fixt.AwayTeam.Venue.Country.Id != null ? fixt.AwayTeam.Venue.Country.Id : 0,
                                 countryName = fixt.AwayTeam.Venue.Country.Id != null ? fixt.AwayTeam.Venue.Country.Name : string.Empty,
                                 compId = fixt.Contest.Competition.Id,
                                 compDesc = fixt.Contest.Competition.Description
                             }).Distinct();

            // ensure that we return the max competition based on id for home teams
            var homemax = (from t in homecomps
                           group t by t.teamId
                               into grp
                               let maxcomp = grp.Max(g => g.compId)
                               from g in grp
                               where g.compId == maxcomp
                               select g).Distinct();

            // ensure that we return the max competition based on id for away teams
            var awaymax = (from t in awaycomps
                           group t by t.teamId
                               into grp
                               let maxcomp = grp.Max(g => g.compId)
                               from g in grp
                               where g.compId == maxcomp
                               select g).Distinct();

            var filteredteams = homemax.Union(awaymax).OrderBy(t => t.teamName).AsQueryable();

如您所见,我们希望返回传递给 WebAPI 的以下格式,因此我们将结果转换为我们可以在 UI 中关联的类型。

基本上,我们想要做的是让主队和客队从赛程中脱颖而出,这些赛程有一场与比赛相关的比赛。然后,我们从分组中获得最高的比赛 ID,然后将其与该团队一起返回。该国家/地区与基于场地ID的团队相关,当我最初这样做时,我在弄清楚如何做或加入linq时遇到了问题,这就是为什么我将其分解为获得主队和客队,然后根据他们进行分组竞争然后将他们联合起来。

当前表格大小的一个概念是夹具有 7840 行,团队有 8581 行,比赛有 337 行,比赛有 96 行。可能快速增加的表是赛程表,因为这与足球有关。

我们最终想要的输出是

团队 ID、团队名称、国家 ID、国家名称、比赛 ID、比赛名称

不使用过滤此查询平均需要大约 5 秒,只是想知道是否有人对如何使其更快有任何想法/指示。

提前谢谢马克

【问题讨论】:

  • 在我看来,EF 不是此类查询的正确工具。可能生成的 sql 语句很糟糕。我建议使用存储过程和/或视图。当然你可以从 EF 调用它。
  • 分析查询计划以确定是否缺少任何索引
  • 您的工作单元模式是否支持延迟加载?如果没有,那将大大提高速度。
  • @DavideIcardi 无论您选择用什么编写查询,如果您做得不好,它在 SQL 中将无法正常工作。 EF 和 SP 也是如此。您应该始终检查生成的 SQL/查询计划,如果它不好,请修复您的查询...

标签: linq entity-framework optimization entity-framework-5


【解决方案1】:

我无法判断它是否会加快速度,但您的homemaxawaymax 查询可能是

var homemax = from t in homecomps
              group t by t.teamId into grp
              select grp.OrderByDescending(x => x.compId).FirstOrDefault();

var awaymax = from t in awaycomps
              group t by t.teamId into grp
              select grp.OrderByDescending(x => x.compId).FirstOrDefault();

此外,当您编写一个非常大的查询时,将其拆分为几个较小的查询来获取中间结果可能会更好。有时,与数据库引擎找不到好的执行计划的一个非常大的查询相比,多几次往返数据库的性能要好。

另一件事是所有这些Distinct()s。你总是需要它们吗?我认为你可以不用,因为你总是从一个表中获取数据而不加入子集合。删除它们可能会节省很多。

另一个优化可能是删除ToUpper。比较是由 SQL 中的数据库引擎完成的,并且很可能数据库具有不区分大小写的排序规则。如果是这样,即使您希望比较也不区分大小写! Name.ToUpper 之类的构造会取消使用 Name 上的任何索引(它不是 sargable)。

【讨论】:

  • 嗨格特,感谢您的回复。关闭延迟加载将其降至 4 秒左右,关闭差异将其降至 3.75 秒左右。当我尝试应用您的分组示例时,它会在 .First() 部分出现错误(查询主体必须以 select 子句或 group 子句结尾),并且在最后一个 linq 部分中的 union 子句上出现错误 - 无法推断类型参数从用法上。所以不确定这是否被错误地复制。
  • 我做了一些修改并添加了另一个我想到的东西。
  • 太棒了,修改了,时间刚刚超过3秒,感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-27
  • 2013-04-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多