c# – 如何执行LINQ GroupBy,选择,然后在没有性能命中的情况下执行?

背景

我有一个SQL数据集,通过LINQ-to-Entities被称为视图.其目的是在信用报告中提供未付30天,未完成60天等的未结帐户余额.

在StackOverflow上为您提供样本表很难格式化,但这里是SQL SELECT语句,它可以让您了解原始数据结构:

SELECT TOP 1000 [TransactionId]
      ,[IndustrySector]
      ,[DataContributorId]
      ,[ExperienceMonth]
      ,[ExperienceMonthText]
      ,[Balance]
      ,[ARCurrent]
      ,[AR1to30PD]
      ,[AR31to60PD]
      ,[AR61to90PD]
      ,[Ar91PlusPD]
      ,[WeightedDTP]
  FROM [BCC].[dbo].[vwTransactionExperienceDetail] 

现在,当我通过LINQ调用此视图时,最终目标是构造一个将作为JSON返回给请求客户端的对象.生成的对象需要按行业分组,然后是贡献者(报告的数据),最后是单个报告.为此,以下LINQ查询工作正常并且非常快:

        /// <summary>
        /// Gets the 25 month experience detail report with summed parameters (balance, DTP, etc).
        /// </summary>
        /// <param name="id">The transaction id.</param>
        /// <returns>List&lt;ExperienceDetail&gt;</returns>
        public static List<ExperienceDetail> Get25MonthExperienceDetail_Sum(int id)
        {
            var db = new BCCEntities();
            return
                db.vwTransactionExperienceDetails.Where(te => te.TransactionId == id)
                  .GroupBy(g => g.IndustrySector)
                  .Select(i => new ExperienceDetail
                      {
                          Industry = i.Key,
                          NumberOfContributors = i.GroupBy(c => c.DataContributorId).Count(),
                          Balance = i.Sum(s => s.Balance),
                          OneToThirty = i.Sum(s => s.ARCurrent),
                          ThirtyOneToSixty = i.Sum(s => s.AR1to30PD),
                          SixtyOneToNinety = i.Sum(s => s.AR31to60PD),
                          NinetyOneToOneTwenty = i.Sum(s => s.AR61to90PD),
                          OneTwentyOnePlus = i.Sum(s => s.Ar91PlusPD),
                          DTP = (i.Sum(s => s.Balance) != 0) ? i.Sum(s => s.WeightedDTP) / i.Sum(s => s.Balance) : i.Sum(s => s.WeightedDTP),
                          Contributions = i.GroupBy(dc => dc.DataContributorId).Select(c => new Contribution
                              {
                                  Balance = c.Sum(s => s.Balance),
                                  OneToThirty = c.Sum(s => s.ARCurrent),
                                  ThirtyOneToSixty = c.Sum(s => s.AR1to30PD),
                                  SixtyOneToNinety = c.Sum(s => s.AR31to60PD),
                                  NinetyOneToOneTwenty = c.Sum(s => s.AR61to90PD),
                                  OneTwentyOnePlus = c.Sum(s => s.Ar91PlusPD),
                                  DTP = (c.Sum(s => s.Balance) != 0) ? c.Sum(s => s.WeightedDTP) / c.Sum(s => s.Balance) : c.Sum(s => s.WeightedDTP),
                                  ContributorId = c.Key,
                                  Reports = c.Select(r => new Report
                                  {
                                      DTP = (r.Balance != 0) ? r.WeightedDTP/r.Balance : r.WeightedDTP,
                                      ReportDate = r.ExperienceMonth,
                                      Balance = r.Balance,
                                      OneToThirty = r.ARCurrent,
                                      ThirtyOneToSixty = r.AR1to30PD,
                                      SixtyOneToNinety = r.AR31to60PD,
                                      NinetyOneToOneTwenty = r.AR61to90PD,
                                      OneTwentyOnePlus = r.Ar91PlusPD,
                                      ContributorId = r.DataContributorId,
                                      Industry = i.Key
                                  })
                              })
                      }).ToList();
        }

问题

我需要创建一个提供相同数据的附加服务,但仅限于每个贡献者(DataContributorId)报告的最近一个月.以下LINQ查询适用于此,但非常慢 – 返回结果需要将近一分钟:

        /// <summary>
        /// Gets an experience detail report with summed parameters (balance, DTP, etc) for the most recent month.
        /// </summary>
        /// <param name="id">The transaction id.</param>
        /// <returns>List&lt;ExperienceDetail&gt;</returns>
        public static List<ExperienceDetail> Get25MonthExperienceDetail_MostRecentMonth(int id)
        {
            var db = new BCCEntities();
            db.CommandTimeout = 100000;
            return
                db.vwTransactionExperienceDetails.Where(te => te.TransactionId == id)
                  .OrderByDescending(o => o.ExperienceMonth)
                  .GroupBy(g => g.IndustrySector)
                  .Select(i => new ExperienceDetail
                  {
                      Industry = i.Key,
                      NumberOfContributors = i.GroupBy(c => c.DataContributorId).Count(),
                      Balance = i.GroupBy(dc => dc.DataContributorId).Sum(x => x.Select(z => z.Balance).FirstOrDefault()),
                      OneToThirty = i.Sum(s => s.ARCurrent),
                      ThirtyOneToSixty = i.Sum(s => s.AR1to30PD),
                      SixtyOneToNinety = i.Sum(s => s.AR31to60PD),
                      NinetyOneToOneTwenty = i.Sum(s => s.AR61to90PD),
                      OneTwentyOnePlus = i.Sum(s => s.Ar91PlusPD),
                      DTP = (i.Sum(s => s.Balance) != 0) ? i.Sum(s => s.WeightedDTP) / i.Sum(s => s.Balance) : i.Sum(s => s.WeightedDTP),
                      Contributions = i.GroupBy(dc => dc.DataContributorId).Select(c => new Contribution
                      {
                          Balance = c.Take(1).Sum(s => s.Balance),
                          OneToThirty = c.Take(1).Sum(s => s.ARCurrent),
                          ThirtyOneToSixty = c.Take(1).Sum(s => s.AR1to30PD),
                          SixtyOneToNinety = c.Take(1).Sum(s => s.AR31to60PD),
                          NinetyOneToOneTwenty = c.Take(1).Sum(s => s.AR61to90PD),
                          OneTwentyOnePlus = c.Take(1).Sum(s => s.Ar91PlusPD),
                          DTP = (c.Take(1).Sum(s => s.Balance) != 0) ? c.Take(1).Sum(s => s.WeightedDTP) / c.Take(1).Sum(s => s.Balance) : c.Take(1).Sum(s => s.WeightedDTP),
                          ContributorId = c.Key,
                          Reports = c.Select(r => new Report
                          {
                              DTP = (r.Balance != 0) ? r.WeightedDTP / r.Balance : r.WeightedDTP,
                              ReportDate = r.ExperienceMonth,
                              Balance = r.Balance,
                              OneToThirty = r.ARCurrent,
                              ThirtyOneToSixty = r.AR1to30PD,
                              SixtyOneToNinety = r.AR31to60PD,
                              NinetyOneToOneTwenty = r.AR61to90PD,
                              OneTwentyOnePlus = r.Ar91PlusPD,
                              ContributorId = r.DataContributorId,
                              Industry = i.Key
                          }).Take(1)
                      })
                  }).ToList();

        }

如何在不影响性能的情况下查询此“报告的最近月份”结果集?我已经尝试过去几个小时来隔离花费最多时间的查询部分,我似乎无法发现它.不可否认,我不知道如何通过复杂的LINQ查询有效地分析性能问题,并且我愿意发表评论.

最终的问题是:这个LINQ查询是否有替代产生相同的结果集而没有如此严重的性能损失?

提前致谢.

最佳答案 假设数据集相当小,我只需要拉入所有月份,转到ToList(),然后过滤掉内存中最近一个月的数据.当查询变得复杂时,LINQ可以做一些非常奇怪的事情.

点赞