2013-01-17 10 views
5

SQL Serverデータベースに対してかなり複雑なSQLクエリが実行されるレポートを作成するように求められました。レポートのサイトはすでにEntity Frameworkの4.1を使用していたので、私はEFとLINQを使用してクエリを記述しようと考えた:このLINQ to Entitiesクエリを最適化する方法はありますか?

var q = from r in ctx.Responses 
        .Where(x => ctx.Responses.Where(u => u.UserId == x.UserId).Count() >= VALID_RESPONSES) 
        .GroupBy(x => new { x.User.AwardCity, x.Category.Label, x.ResponseText }) 
     orderby r.FirstOrDefault().User.AwardCity, r.FirstOrDefault().Category.Label, r.Count() descending 
     select new 
     { 
      City = r.FirstOrDefault().User.AwardCity, 
      Category = r.FirstOrDefault().Category.Label, 
      Response = r.FirstOrDefault().ResponseText, 
      Votes = r.Count() 
     }; 

このクエリは、票を集計が、唯一の特定の数を提出したユーザーから必要最低限​​の票。

このアプローチはパフォーマンスの観点からは完全な災害であったため、ADO.NETに切り替え、クエリは非常に迅速に実行されました。私はSQLプロファイラを使用してLINQを生成したSQLを見ましたが、いつものように荒々しく見えましたが、LINQ文を最適化してより効率的にする方法についての手がかりは見つかりませんでした。 EFおよびLINQを効率的に処理するために、または私はトリックを見逃しているため、このクエリでは、あまりにも複雑さ:

WITH ValidUsers(UserId) 
AS 
(
    SELECT UserId 
    FROM Responses 
    GROUP BY UserId 
    HAVING COUNT(*) >= 103 
) 
SELECT d.AwardCity 
    , c.Label 
    , r.ResponseText 
    , COUNT(*) AS Votes 
FROM ValidUsers u 
JOIN Responses r ON r.UserId = u.UserId 
JOIN Categories c ON r.CategoryId = c.CategoryId 
JOIN Demographics d ON r.UserId = d.Id 
GROUP BY d.AwardCity, c.Label, r.ResponseText 
ORDER BY d.AwardCity, s.SectionName, COUNT(*) DESC 

私は思ったんだけど次のとおりです。

ここでストレートTSQLのバージョンがありますか?

+0

私はFirstOrDefaultsがそれを引き起こしていると推測しています。 groupbyの前に 'let response = r.First()'を追加しようとしましたか? SelectとOrderByを入れ替える?このようにhttp://stackoverflow.com/a/5013740/736079 – jessehouwing

+2

'User.Responses'のようなナビゲーションプロパティはありますか? –

+0

@ letess応答を使用するjessehouwingは、LINQのバージョンはまだADO.NETよりもはるかに遅いですが、かなり役立ちます。これを回答として入力すると、少なくともアップアップします。私はJon Skeetの選択と注文を入れ替える戦略に問題があります。主に、この構成でカウントを得る方法を見つけることができません。 –

答えて

4

に入れてどこに選択取り外し、パフォーマンスを向上させますr.First()の数がパフォーマンスを向上させるでしょう。おそらくまだ十分ではないでしょう。

var q = from r in ctx.Responses 
       .Where() 
       .GroupBy() 
    let response = r.First() 
    orderby response.User.AwardCity, response.Category.Label, r.Count() descending 
    select new 
    { 
     City = response.User.AwardCity, 
     Category = response.Category.Label, 
     Response = response.ResponseText, 
     Votes = r.Count() 
    }; 
+0

これは効果が@GertArnoldによって提案されたナビゲーションプロパティソリューションとほぼ同等であるため、これを答えとしてマークしていますが、Gertは答えとしてコメントを投稿していません(申し訳ありませんが、私はあなたにアップアップします)。両方の最適化を適用しても、ADO.NETはまだ高速ですが、修正されたLINQはそれよりもはるかに高速です。 –

+1

コメントをアップすることもできます。 – jessehouwing

1

たぶん、この変更は、得られた、ネストされたSQLが句

まず、各ユーザの票を取得し、削減にLETを使用しDictionary

var userVotes = ctx.Responses.GroupBy(x => x.UserId) 
          .ToDictionary(a => a.Key.UserId, b => b.Count()); 

var cityQuery = ctx.Responses.ToList().Where(x => userVotes[x.UserId] >= VALID_RESPONSES) 
       .GroupBy(x => new { x.User.AwardCity, x.Category.Label, x.ResponseText }) 
       .Select(r => new 
         { 
          City = r.First().User.AwardCity, 
          Category = r.First().Category.Label, 
          Response = r.First().ResponseText, 
          Votes = r.Count() 
         }) 
       .OrderByDescending(r => r.City, r.Category, r.Votes()); 
+0

私はこのアプローチを既に試していました。 LINQ to Entitiesメソッドが 'Int32 get_Item(Int32)'メソッドを認識せず、このメソッドをストア式に変換することはできません –

+0

ctx.Responsesの後にToList()を追加することができませんでした。 ctx.Responses.ToList()。Where(.... –

+0

私は、EF LINQドライバがエンティティSQLを構築している間に、外部ディクショナリへの参照を使用できないという重大な問題があると思います。あなたのソリューションを文字通り貼り付けると、最初のToDictionaryラムダの構文が修正されました。 –

関連する問題