2012-10-11 2 views
3

J'ai cette requête linq que j'utilise et cela prend 50 secondes à courir quand je l'exécute mon application asp.net, mais la même requête s'exécute en 500ms dans LinqPad et Sql Management Studio.Requête lente dans Linq, rapide dans LinqPad, SQL Management Studio et SQL Profiler

J'ai même pris la requête du profileur SQL et l'ai réexécutée dans SQL Management Studio et cela prend environ 500ms. Quels frais généraux Linq pourrait-il faire, que 49 supplémentaires ??

Ci-dessous le code de référence, merci pour votre aide.

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

requête impressionnante. Y a-t-il une raison pour laquelle vous avez décidé de commencer avec la syntaxe Query et de terminer avec la syntaxe de la méthode? J'ai du mal à suivre ce que fait votre requête. Si vous réécrivez la requête dans la syntaxe de requête, il peut être plus facile de repérer certaines optimisations. – Aducci

+0

Pourriez-vous publier le code SQL généré? –

+0

@Thom Smith, à partir de sql profiler https://gist.github.com/3872906, sa ligne 1500, mais fonctionne encore en moins d'une seconde –

Répondre

1

Il y a quelques façons dont vous pouvez simplifier cette requête:

  • select into vous permet de garder tout dans la syntaxe de requête.
  • Les constructions join ... into/from/DefaultIfMany implémentant des jointures à gauche peuvent être remplacées par des constructions join ... into représentant des jointures de groupe.
  • Certains des groupes proches de la fin ne peuvent pas être vides, donc FirstOrDefault est inutile.
  • Certaines des conditions where peuvent être déplacées vers le haut avant que la requête ne se complique.

Voici le coup de poignard que j'ai pris. Les révisions ont été importantes, il aurait besoin d'un peu de débogage:

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() 
+0

requête brute de SQL Profiler est maintenant de 390 lignes, prend moins d'une seconde à exécuter dans asp.net, merci à tous. Mais pourquoi l'ancienne requête s'exécutait-elle en mssql en une fraction de seconde et en asp.net en 50 secondes, où était la surcharge? –

+0

Ma conjecture: avoir les deux jointures à gauche entrelacées ('join ... dans' - 'join ... into'-'from'-'from' plutôt que' join ... into'-'from'-'join ... into'-'from') le confondait, et vous n'obteniez pas une simple jointure à gauche. –

1

J'ai refaçonné en utilisant la syntaxe Query (Je ne sais pas si cela améliore la lisibilité). Supprimé un groupe par. Fait quelques ajustements mineurs (remplacé FirstOrDefault avec la propriété Key, changé Contient à tout). Espérons que cela a un effet de vitesse.

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