2011-01-20 147 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