2016-05-09 4 views
3

linqには実行に時間がかかりすぎる(2分以上)大きな結合ステートメントがあります。データベースが正しく設定され、データが正しく取得されているかどうか試してみる方法でしたが、プロジェクトはほぼ完了しており、そのような遅延は受け入れられません。linqで効率的に文字列を結合するには?

問題はむしろ明らかにString.Joinのステートメントですが、私は別の方法でそれを行う方法を知らないので、私の質問はどのように同じ情報を得るためにリンクステートメントを変更することができますが、より速い方法です。私は、参加する必要があるエンティティの1つを作成する際に、データに参加して別の場所に格納するほうが効果的かもしれないと考えていましたが、可能であれば、このステートメントをより良くする方がよいでしょう。

「B」又は「M」など

var dc = datacontext; 
var resultSet = (
    from r in dc.requests 
    select new ProjectQueryElement { 
     bKey = String.Join("|", dc.comps 
        .Where(
         x => x.reRequestId == r.requestId && x.function == "b" 
        ).Select(x => x.idNumber) 
       ), 
     mKey = String.Join("|", dc.comps 
        .Where(
         x => x.reRequestId == r.requestId && x.function == "m" 
        ).Select(x => x.idNumber) 
       ), 
     oKey = String.Join("|", dc.comps 
        .Where(
         x => x.reRequestId == r.requestId && x.function == "o" 
        ).Select(x => x.idNumber) 
       ), 
     pKey = String.Join("|", dc.comps 
        .Where(
         x => x.reRequestId == r.requestId && x.function == "p" 
        ).Select(x => x.idNumber) 
       ), 
     rKey = String.Join("|", dc.comps 
        .Where(
         x => x.reRequestId == r.requestId && x.function == "r" 
        ).Select(x => x.idNumber) 
       ) 
     } 
); 

resultSetは、グリッドの行としてjqgridに渡されるの機能をProjectQueryElementための構成要素を持たないことが可能です。

+7

を私はあなただけの要素を気にすることを考えると、 'r'と' dc.comps'の間の結合を導入することから始めたいです'dc.comps'ここで' reRequestId'は 'r.requestId'です...しかし、あなたはSQLの外観を見ましたか? –

+0

Jon Skeetの答えに加えて、私は 'StringBuilder'を導入し、それらの助けを借りて文字列を構築することを提案します。 – ckruczek

+1

Manfredの回答のコメントを参照してください。@ckruczek –

答えて

2

問題は、むしろ当然私はString.Joinがボトルネックになっている疑いString.Join

ですそのメソッドに渡されるサブクエリが呼び出されます。

合計N件のリクエストと合計M件のリクエストがあるとします。 LINQ to Objectsでは、クエリで5 * N * Mの線形検索が実行されます。データベースクエリ可能なプロバイダの変換されたSQLクエリは5 * Nサブクエリを実行し、適切なインデックスがない場合、結果の時間の複雑さはLINQ to Objectsと似ています(フルテーブルスキャンのためさらに悪化します)。

IMOデータ検索ロジックとデータ変換ロジックを分離するのが最適です。 @Jon Skeetがコメント内で直ちに指摘したもの)と投影(選択)した後、メモリ内でデータ変換(文字列変換/結合)を行い、dbクエリ結果を順番に処理すること(AsEnumerable()ToList等)。この時点で

var resultSet = 
    (from r in dc.requests 
    join c in dc.comps on r.requestId equals c.reRequestId into requestComps 
    select requestComps.Select(c => new { c.function, c.idNumber })) 
    .AsEnumerable() 
    .Select(requestComps => new ProjectQueryElement 
    { 
     bKey = string.Join("|", requestComps.Where(c => c.function == "b").Select(c => c.idNumber)), 
     mKey = string.Join("|", requestComps.Where(c => c.function == "m").Select(c => c.idNumber)), 
     oKey = string.Join("|", requestComps.Where(c => c.function == "o").Select(c => c.idNumber)), 
     pKey = string.Join("|", requestComps.Where(c => c.function == "p").Select(c => c.idNumber)), 
     rKey = string.Join("|", requestComps.Where(c => c.function == "r").Select(c => c.idNumber)), 
    }); 

IMOあなたが素敵な、高速実行中のクエリを持っている必要があります。このすべてを適用

はこのような何かをもたらすことになります。

本当に必要な場合は、変換部分(最後のSelect)を最適化することができます。 string.Joinは内部的に最適化されているため、requestCompsセットに対して1回の最適化を行うことができますが、メモリ内で実行されているため、パフォーマンスへの影響はごくわずかです。あなたが実際の影響を測定して見ることができるようにちょうど完全を期すため、その部分を提供する:

var query = 
    // ... 
    .Select(requestComps => 
    { 
     StringBuilder bKey = null, mKey = null, oKey = null, pKey = null, rKey = null; 
     foreach (var c in requestComps) 
     { 
      switch (c.function) 
      { 
       case "b": bKey = (bKey != null ? bKey.Append("|") : new StringBuilder()).Append(c.idNumber); break; 
       case "m": mKey = (mKey != null ? mKey.Append("|") : new StringBuilder()).Append(c.idNumber); break; 
       case "o": oKey = (oKey != null ? oKey.Append("|") : new StringBuilder()).Append(c.idNumber); break; 
       case "p": pKey = (pKey != null ? pKey.Append("|") : new StringBuilder()).Append(c.idNumber); break; 
       case "r": rKey = (rKey != null ? rKey.Append("|") : new StringBuilder()).Append(c.idNumber); break; 
      } 
     } 
     return new ProjectQueryElement 
     { 
      bKey = bKey != null ? bKey.ToString() : string.Empty, 
      mKey = mKey != null ? mKey.ToString() : string.Empty, 
      oKey = oKey != null ? oKey.ToString() : string.Empty, 
      pKey = pKey != null ? pKey.ToString() : string.Empty, 
      rKey = rKey != null ? rKey.ToString() : string.Empty, 
     }; 
    }); 
0

あなたはLINQのを使用しているので、あなただけの代わりにAggregate機能を使用することもできます。

xKey = dc.comps 
    .Where(x => x.reRequestId == r.requestId && x.function == "b") 
    .Aggregate((result, next) => result + "|" + next.idNumber); 
+3

これは、余分な文字列を作成します。私はこれが問題を悪化させると思う。 –

+0

@PatrickHofman実際にはそれをSQLに変える必要がありますが、それは違いがあるとは思えません。 – juharr

+0

@PatrickHofman確かに、 'String.Join'を含んでいないので、データベース上でクエリを実行できるようになりました –

関連する問題