2011-01-20 183 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) 

and

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

現在在這種特殊情況下,我們需要返回50,000行......這是通過5個獨立的查詢生成的,可以分解負載。 數據庫返回相當快(在幾秒鐘內),但生成查詢需要時間。

這裏是被稱爲產生這種特定查詢的最後一個功能:

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

不知道爲什麼你're doing list.Cast 當一個純IEnumerable將無論如何將是對象。 – Massif 2011-01-20 09:49:00

回答

5

你的聚合代碼基本上是一個循環中的字符串連接。不要這樣做。

選項:

  1. 使用StringBuilder
  2. 使用的string.join
+2

解釋這個背後的原因:字符串連接爲每個級聯分配一個新的內存緩衝區,將BOTH字符串複製到該緩衝區並將新緩衝區分配給該變量。循環長列表會導致massiv內存分配和複製,這是非常昂貴的performancevize。 StringBuilder和string.join會預先計算所有元素所需的總空間並只複製一次。 – 2011-01-20 10:06:04

1

我還沒有做出測試用例和異形你的代碼,所以我不知道有多少改善,你可以期望。

使用StringBuilder而不是String.Format和+ =運算符。 + =運算符已知很慢。我懷疑String.Format也會有點慢。

您也可以嘗試string.Join而不是手動加入數組。它適用於較新版本的.NET框架(4.0?)中的IEnumerable。

+0

String.Format內部使用StringBuilder(http://stackoverflow.com/questions/6785/is-string-format-as-efficient-as-stringbuilder),但由於它使用了許多不同的時間,我同意你的觀點。 – 2011-01-20 09:38:23

+0

多個string.format將會像+你一樣緩慢地分配新的內存並複製數據。當您執行ToString調用時,字符串構建器只會分配內存和副本。 – 2011-01-20 10:08:16

0

不知道你爲什麼要做list.Cast,當一個普通的IEnumerable無論如何都是對象。但是你的整個ListToDBList可以被替換爲

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

不知道它會更快,但它更清楚我的想法。

2

這是一個使用String的例子。加入輸出與您的ListToDBList:

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

在這裏看到一個解釋,爲什麼在循環中使用+(這是你對彙總調用在做)串聯緩慢:http://www.yoda.arachsys.com/csharp/stringbuilder.html