在Linq中查询速度慢,在LinqPad,SQL Management Studio和SQL Profiler中快速查询

时间:2012-10-11 14:02:41

标签: sql-server linq

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

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

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

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();

2 个答案:

答案 0 :(得分:1)

我使用Query语法重构(不确定它是否提高了可读性)。删除了一个组。做了一些小的调整(用Key属性替换了FirstOrDefault,将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();

答案 1 :(得分:1)

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

  • select into可让您将所有内容保存在查询语法中。
  • 实现左连接的join ... into / from / DefaultIfMany构造可以替换为代表组连接的join ... 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()