2013-05-10 100 views
5

我使用下面的查询慢LINQ查询()

foreach (var callDetailsForNode_ReArrange in callDetailsForNodes_ReArrange) 
{ 
    var test = from r1 in dtRowForNode.AsEnumerable() 
       join r2 in dtFileRowForNode.AsEnumerable() 
       on r1.Field<int>("Lng_Upload_Id") equals r2.Field<int>("Lng_Upload_Id") 
       where ((r1.Field<string>("Txt_Called_Number") == callDetailsForNode_ReArrange.caller2.ToString()) || r1.Field<string>("Txt_Calling_Number") == callDetailsForNode_ReArrange.caller2.ToString()) 
       select r2.Field<string>("Txt_File_Name"); 

    var d = test.Distinct(); 
} 

高达这里没有这个时间查询运行。但正如我加

string[] str =d.ToArray(); 
strFileName = string.Join(",", str); 

它需要几乎4-5秒的运行。是什么让它加入.ToArray()这么慢?

+1

阅读第一个答案[此问题](http://stackoverflow.com/q/215548/601179) – gdoron 2013-05-10 12:01:52

+1

.Distinct()不运行查询吗? – 2013-05-10 12:03:40

+1

没有。它只是增加了一个事实,即你只需要每个元素一次就可查询。 – 2013-05-10 12:05:14

回答

15

高达此查询运行立刻。

到这里,它实际上并没有做什么,除了建表示未决查询延期执行模型。它不会开始迭代,直到您在迭代器上调用MoveNext(),即通过foreach,在您的案例中通过.ToArray()

所以:这是需要时间,因为它是做工

考虑:

static IEnumerable<int> GetData() 
{ 
    Console.WriteLine("a"); 
    yield return 0; 
    Console.WriteLine("b"); 
    yield return 1; 
    Console.WriteLine("c"); 
    yield return 2; 
    Console.WriteLine("d"); 
} 
static void Main() 
{ 
    Console.WriteLine("start"); 
    var data = GetData(); 
    Console.WriteLine("got data"); 
    foreach (var item in data) 
     Console.WriteLine(item); 
    Console.WriteLine("end"); 
} 

此输出:

start 
got data 
a 
0 
b 
1 
c 
2 
d 
end 

注意如何工作不一切发生一次 - 它既是延迟(a自带got data后)和后台(我们不't得到a,...,d0,... 2)。


相关:这是大致如何Distinct()作品,从评论:

public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source) { 
    var seen = new HashSet<T>(); 
    foreach(var item in source) { 
     if(seen.Add(item)) yield return item; 
    } 
} 

...

和新Join操作:

public static string Join(this IEnumerable<string> source, string separator) { 
    using(var iter = source.GetEnumerator()) { 
     if(!iter.MoveNext()) return ""; 
     var sb = new StringBuilder(iter.Current); 
     while(iter.MoveNext()) 
      sb.Append(separator).Append(iter.Current); 
     return sb.ToString(); 
    } 
} 

及用途:

string s = d.Join(","); 
+2

我不知道即使是方法'Console.WriteLine(“a”);'的第一行也不会在'foreach'之前打印。所以我会认为'a'出现在'有数据'之前。有时Linq仍然令人惊讶。 – 2013-05-10 12:23:52

12

因为查询在您迭代它之前没有任何操作,所以.ToArray()会这样做。

需要注意的一件事是,即使只有结果的第一个元素是开始迭代,连接的右侧(在您的示例中为r2 in dtFileRowForNode.AsEnumerable())将被完全枚举为ASON被访问 - 但直到那时。

所以,如果你这样做:

d.First()

r2 in dtFileRowForNode.AsEnumerable()序列将全面迭代(和内存中缓存),但只有r1 in dtRowForNode.AsEnumerable()的第一个元素将被评估。因为这个原因,如果连接中的一个序列比另一个序列大得多,那么将大序列放在连接的左侧会更有效(记忆方式)。连接右侧的整个序列将被缓存在内存中。

(我要指出的是仅适用于LINQ到对象。LINQ到SQL将会在数据库中运行这些查询,所以它处理的缓冲。)

+0

^^他说了什么。当你定义查询时,你正在做的是 - 定义**查询。你没有运行它来获得结果。当你调用'.ToArray()'时,查询会执行并且所有需要的处理都会发生(这需要时间)。然后你有一系列的结果。 – theyetiman 2013-05-10 12:01:33

+0

有没有什么办法可以在逗号分隔的字符串中有效地得到不同的值 – 2013-05-10 12:01:53

+0

.Distinct()是否不运行查询? – 2013-05-10 12:04:45

2

你需要阅读递延评价linq声明。查询不会完成,除非您明确要求结果 - 例如在foreach中迭代,调用ToArrayToListSum,First或其他评估查询的方法之一。

所以它是你的查询,需要这么多时间来完成,而不是ToArray调用。