2011-01-20 19 views
2

現在、LINQを使用してSQLクエリーを生成しています。文字列連結の最適化

これまでのところ、うまくいきました。非常に速く、ほとんど問題はありません。私たちは最近、データベースから大量のデータを照会する際に、効率の問題に遭遇しました。

我々のようなクエリ構築:

var someIntList = new List<int> { 1,2,3,4,5 }; 
var query = dtx.Query.Containers.Where(c => c.ContainerID.IsIn(someIntList)); 

または

(これに関連していない他の原料の束と一緒に)生成されます
var someStringList = new List<int> {"a", "b", "c" }; 
query = dtx.Query.Containers.Where(c => c.BuildingName.IsIn(someStringList)); 

SELECT * FROM Container WHERE ContainerID IN (1,2,3,4,5) 

および

SELECT * FROM Container WHERE BuildingName IN ('a','b','c') 

この特定の状況では、負荷を分割する5つの別個のクエリによって生成される50,000行を返す必要があります。 DBはかなり速く(数秒以内に)返されますが、クエリの生成にはの時間がかかります()。ここで

は、この特定のクエリを生成するために呼び出される非常に最後の関数です:

private static string GetSafeValueForItem(object item) 
{ 
    if (item == null) 
     return "NULL"; 

    if (item is bool) 
     return ((bool)item ? "1" : "0"); 
    if (item is string) 
     return string.Format("'{0}'", item.ToString().Replace("'", "''")); 
    if (item is IEnumerable) 
     return ListToDBList((IEnumerable)item); 
    if (item is DateTime) 
     return string.Format("'{0}'", ((DateTime)item).ToString("yyyy-MM-dd HH:mm:ss")); 

    return item.ToString(); 
} 

private static string ListToDBList(IEnumerable list) 
{ 
    var str = list.Cast<object>().Aggregate("(", (current, item) => current + string.Format("{0},", GetSafeValueForItem(item))); 
    str = str.Trim(','); 
    str += ")"; 
    return str; 
} 

は、この場合には、文字列の連結をスピードアップさせることができる任意の明白な改善点はありますか?コードをリファクタリングし、別の実装(クエリの生成やデータベースへの直接的なアクセスを避けるなど)を使用することは好ましくありませんが、パフォーマンスが大幅に向上した場合はそれを聞くのが良いでしょう。

+0

とにかく普通のIEnumerableがオブジェクトであるときにリスト012を作成しています。 Massif

答えて

5

あなたの集計コードは基本的にループ内の文字列連結です。それをしないでください。

オプション:

  1. 使用StringBuilder
  2. 使用string.Join
+2

これの背後にある理由を説明するために、文字列連結は各連結に対して新しいメモリバッファを割り当て、それにBOTH文字列をコピーして新しいバッファを変数に割り当てます。長いリストをループすると、非常に高価なperformancevizeである大量のメモリ割り当てとコピーが発生します。 StringBuilderとstring.joinは、すべての要素に必要な合計領域を事前に計算し、それらを1回だけコピーします。 –

1

私はテストケースを作り、あなたのコードをプロファイリングので、私はどれだけ改善することができます知っていないしていません期待する。

String.Formatおよび+ =演算子の代わりにStringBuilderを使用します。 + =演算子は遅いことが知られています。私はString.Formatもやや遅くなるだろうと思う。

手動で配列を結合する代わりにstring.Joinを試すこともできます。新しいバージョンの.NET Framework(4.0?)ではIEnumerableで動作します。

+0

String.FormatはStringBuilderを内部的に使用しますが(http://stackoverflow.com/questions/6785/is-string-format-as-efficient-as-stringbuilder)、それは多くの別々の時間に使用されるため、私はあなたの意見に同意します。 –

+0

複数のstring.formatは、繰り返し新しいmemryを割り当ててデータをコピーするのと同じくらい+ =遅くなります。文字列ビルダーは、ToString呼び出しを実行するときにのみ、メモリとコピーを最後に割り当てます。 –

0

普通のIEnumerableがオブジェクトである場合は、なぜあなたがlist.Castをやっているのかわかりません。しかし、あなたの全体ListToDBListは

string.Format("({0})", string.Join(",",list.ToArray())); 

それがどのくらい速くわからないで置き換えることができますが、それは私の心に明確です。

+0

IEnumerableがIEnumerable を実装していないため、キャストが必要です。 .NETの3.5では、最初にキャストを使用してIEnumerable に変換せずにLinq拡張メソッドをIEnumerableに適用することはできません。 – Weeble

2

ここでは、Stringを使用した例を示します。それはあなたのListToDBListと同じ出力入会:

String.Format("({0})", String.Join(",", list.Cast<object>().Select(item=>GetSafeValueForItem(item)).ToArray())); 

(集計へのお電話が何をしていたかである)+を使用してループに連結する理由を説明についてはこちらを参照して遅いです:なぜあなたがわからないhttp://www.yoda.arachsys.com/csharp/stringbuilder.html