3

我正在使用这个 linq 查询,当我在我的 asp.net 应用程序中运行它时,它需要 50 秒才能运行,但是在 LinqPad 和 Sql Management Studio 中,相同的查询在 500 毫秒内执行。

我什至从 SQL Profiler 中获取查询并在 SQL Management Studio 中再次运行它,大约需要 500 毫秒。Linq 可以做什么开销,额外的 49 秒?

下面是代码供参考,感谢您的帮助。

var rCampaign =
    (from a in db.AdCreative
     join h in db.AdHit on a.ID equals h.AdID into gh
     join l in db.AdGroup_Location on a.AdGroupID equals l.AdGroupID into gj
     from subloc in gj.DefaultIfEmpty()
     from subhits in gh.DefaultIfEmpty()
     where a.AdGroup.AdHost.Select(q => q.ID).Contains(rPlatform.ID) &&
           a.AdGroup.AdPublisher.Select(q => q.ID).Contains(rPublisher.ID) &&
           a.AdDimensionID == AdSize &&
           a.AdGroup.Campaign.Starts <= rNow &&
           a.AdGroup.Campaign.Ends >= rNow &&
           subhits.HitType == 1 &&
           (subloc == null || subloc.LocationID == rLocationID)
     select new {
         ID = a.ID,
         Name = a.Name,
         Spent = (subhits.AdDimension != null) ? ((double)subhits.AdDimension.Credit / 1000) : 0,
         CampaignID = a.AdGroup.Campaign.ID,
         CampaignName = a.AdGroup.Campaign.Name,
         CampaignBudget = a.AdGroup.Campaign.DailyBudget
     }).GroupBy(adgroup => adgroup.ID)
       .Select(adgroup => new {
           ID = adgroup.Key,
           Name = adgroup.FirstOrDefault().Name,
           Spent = adgroup.Sum(q => q.Spent),
           CampaignID = adgroup.FirstOrDefault().CampaignID,
           CampaignName = adgroup.FirstOrDefault().CampaignName,
           CampaignBudget = adgroup.FirstOrDefault().CampaignBudget,
       })
      .GroupBy(q => q.CampaignID)
      .Select(campaigngroup => new {
          CampaignID = campaigngroup.Key,
          DailyBudget = campaigngroup.FirstOrDefault().CampaignBudget,
          Consumed = campaigngroup.Sum(q => q.Spent),
          RemainningCredit = campaigngroup.FirstOrDefault().CampaignBudget - campaigngroup.Sum(q => q.Spent),
          Ads = campaigngroup.Select(ag => new {
              ID = ag.ID,
              Name = ag.Name,
              Spent = ag.Spent
          }).OrderBy(q => q.Spent)
      })
      .Where(q => q.Consumed <= q.DailyBudget).OrderByDescending(q => q.RemainningCredit).First();
4

2 回答 2

1

我使用查询语法进行了重构(不确定它是否提高了可读性)。删除了一组。做了一些小的调整(将 FirstOrDefault 替换为 Key 属性,将 Contains 更改为 Any)。希望它对速度有一些影响。

 var rCampaign = (from cg in 
                   (from a in db.AdCreative
                    from subhits in db.AdHit.Where(h => a.ID == h.AdID)
                                            .DefaultIfEmpty()
                    from subloc in db.AdGroup_Location.Where(l => a.AdGroupID == l.AdGroupID)
                                                      .DefaultIfEmpty()
                    where a.AdGroup.AdHost.Any(q => q.ID == rPlatform.ID) &&
                          a.AdGroup.AdPublisher.Any(q => q.ID == rPublisher.ID) &&
                          a.AdDimensionID == AdSize &&
                          a.AdGroup.Campaign.Starts <= rNow &&
                          a.AdGroup.Campaign.Ends >= rNow &&
                          subhits.HitType == 1 &&
                          (subloc == null || subloc.LocationID == rLocationID)
                    group new { a, subhits } by new { ID = a.ID, a.Name, CampaignID = a.AdGroup.Campaign.ID, CampaignName = a.AdGroup.Campaign.Name, CampaignBudget = a.AdGroup.Campaign.DailyBudget } into g
                    select new 
                    {
                      ID = g.Key.ID,
                      Name = g.Key.Name,
                      Spent = g.Sum(x => (x.subhits.AdDimension != null) ? ((double)subhits.AdDimension.Credit / 1000) : 0),
                      CampaignID = g.Key.CampaignID,
                      CampaignName = g.Key.CampaignName,
                      CampaignBudget = g.Key.CampaignBudget                                    
                   })
                 group cg by new { cg.CampaignID, cg.CampaignBudget } into cg
                 let tempConsumed = cg.Sum(q => q.Spent)
                 let tempRemainningCredit = cg.Key.CampaignBudget - tempConsumed
                 where tempConsumed <= cg.Key.CampaignBudget
                 orderby tempRemainningCredit desc
                 select new
                 {
                   CampaignID = cg.Key.CampaignID,
                   DailyBudget = cg.Key.CampaignBudget,
                   Consumed = tempConsumed,
                   RemainningCredit = tempRemainningCredit,
                   Ads = from ag in cg
                         orderby ag.Spent
                         select new
                         {
                           ID = ag.ID,
                           Name = ag.Name,
                           Spent = ag.Spent
                          }
                  }).First();
于 2012-10-11T15:01:54.953 回答
1

有几种方法可以简化该查询:

  • select into让您将其全部保存在查询语法中。
  • join ... into//实现左连接的构造可以替换为表示组连接的构造fromDefaultIfManyjoin ... into
  • 靠近末尾的某些组不能为空,因此FirstOrDefault没有必要。
  • 在查询变得复杂之前,可以将某些where条件移到顶部。

这是我采取的措施。修订非常重要,因此可能需要进行一些调试:

var rCampaign = ( 
    from a in db.AdCreative

    where a.AdDimensionID == AdSize &&
          a.AdGroup.Campaign.Starts <= rNow &&
          a.AdGroup.Campaign.Ends >= rNow &&
          a.AdGroup.AdHost.Select(q => q.ID).Contains(rPlatform.ID) &&
          a.AdGroup.AdPublisher.Select(q => q.ID).Contains(rPublisher.ID)

    join hit in db.AdHit.Where(h => h.HitType == 1 && h.LocationID == rLocationID)
        on a.ID equals hit.AdID
        into hits

    join loc in db.AdGroup_Location
        on a.AdGroupID equals loc.AdGroupID
        into locs

    where !locs.Any() || locs.Any(l => l.LocationID == rLocationID)

    select new {
        a.ID,
        a.Name,
        Spent = hits.Sum(h => h.AdDimension.Credit / 1000) ?? 0,
        CampaignID = a.AdGroup.Campaign.ID,
        CampaignName = a.AdGroup.Campaign.Name,
        CampaignBudget = a.AdGroup.Campaign.DailyBudget,
    } into adgroup
    group adgroup by adgroup.CampaignID into campaigngroup
    select new
    {
        CampaignID = campaigngroup.Key,
        DailyBudget = campaigngroup.First().CampaignBudget,
        Consumed = campaigngroup.Sum(q => q.Spent),
        RemainingCredit = campaigngroup.First().CampaignBudget - campaigngroup.Sum(q => q.Spent),
        Ads = campaigngroup.Select(ag => new {
            ag.ID,
            ag.Name,
            ag.Spent,
        }).OrderBy(q => q.Spent)
    } into q
    where q.Consumed <= q.DailyBudget
    orderby q.RemainingCredit desc)
    .First()
于 2012-10-11T15:17:42.870 回答