4

バックグラウンド

LINQ-to-Entities を介してビューとして呼び出される SQL データセットがあります。その目的は、信用報告書に 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 として返されるオブジェクトを構築することです。Industry結果として得られるオブジェクトは、グループ化の階層である必要がありContributorsますReports。これを行うには、次の 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 クエリはこれに対して機能しますが、非常に遅く、結果を返すのに 1 分近くかかります。

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

        }

質問

パフォーマンスに影響を与えずに、この "Most Recent Month Reported" の結果セットを照会するにはどうすればよいですか? 過去数時間にわたって、最も時間がかかっているクエリの部分を分離しようとしましたが、それを見つけることができないようです. 確かに、複雑な LINQ クエリでパフォーマンスの問題を効果的にプロファイルする方法がわかりません。コメントを受け付けています。

最終的な問題は次のとおりです。この LINQ クエリに代わるもので、このような深刻なパフォーマンスの低下なしに同じ結果セットを生成するものはありますか?

前もって感謝します。

4

2 に答える 2

1

データセットが適度に小さいと仮定すると、すべての月を取得して移動ToList()し、メモリ内の最新の月だけを除外します。クエリが複雑になると、LINQはいくつかの非常に奇妙なことを行うことができます。

于 2013-01-04T21:41:26.137 に答える
1

追加した2番目のクエリで:

Balance = i.GroupBy(dc => dc.DataContributorId).Sum(x => x.Select(z => z.Balance).FirstOrDefault()),

.OrderByDescending(o => o.ExperienceMonth)

groupBy と orderBy を削除して、それらのいずれかがパフォーマンスの問題を引き起こしているかどうかを確認してください。この場合、この列にインデックス (存在しない場合) を追加することを確認 (および試行) します。

SQL プロファイラー (SQL Server 2005 以前の場合) または SQL 拡張イベント (SQL Server 2008 以降の場合) も確認してください。

ツールLinqPADを試して、クエリによって生成されたSQL DMLを確認できます

データベースからデータを取得する方法は他にもあります。

  1. データベースにビューを作成し、これを LINQ から読み取る
  2. Entity SQLクエリを記述する
于 2013-01-04T17:09:56.423 に答える