1

我有一段代码执行一个相当大的查询,有很多连接等。为简单起见,这里或多或少是结构:

var rootQuery = MainQuery(); // IQueryable<MyClass>
var jq1 = JoinQuery1(); // IQueryable<anonymous type>
var jq2 = JoinQuery2();
...
var jq6 = JoinQuery6();
var bigQuery = from x in rootQuery
               join j1 in jq1 on x.ID equals j1.MainID into join1
               from j1 in join1.DefaultIfEmpty()
               join j2 in jq2 on x.ID equals j2.MainID into join2
               from j2 in join1.DefaultIfEmpty()
               ...
               join j6 in jq6 on x.ID equals j6.MainID into join6
               from j6 in join1.DefaultIfEmpty()
               select new {
                            x.ID,
                            x.Field1,
                            j1.FieldA,
                            j2.FieldB,
                            ...
                            j6.FieldF
                          };
var sw = Stopwatch.StartNew();
var loadedData = bigQuery.ToList();
sw.Stop();
Console.WriteLine("Time Elapsed = {0} ms", sw.ElapsedMilliseconds);

秒表显示经过 30 秒,返回 9 行和大约 30 列数据(没有文本字段)。所以我使用 SQL Server Profiler 嗅探了查询,确实它是一个怪物。我摆弄、刺激和优化了一些索引,并让查询在 200 毫秒内执行。但是当我运行代码时,它仍然需要 30 多秒才能运行.ToList(),尽管 SQL Profiler 说查询部分需要不到 200 毫秒!

这里发生了什么?为什么 .ToList 需要这么长时间才能将这么小的数据集加载到内存中?

编辑:我解决了这个问题(请参阅下面的答案),但我不满意。任何人都可以提出更好的方法,或者至少解释为什么对象物化如此昂贵,以至于运行 7 个单独的查询并将它们连接到本地内存中会更便宜。

4

1 回答 1

1

这似乎非常违反直觉,但我通过单独运行 IQueryables 解决了这个问题(使用 Jon Skeet 的整洁NullOr扩展):

var rootQuery = MainQuery().ToList();
var jq1 = JoinQuery1().ToList();
var jq2 = JoinQuery2().ToList();
...
var jq6 = JoinQuery6().ToList();
var bigQuery = from x in rootQuery
           join j1 in jq1 on x.ID equals j1.MainID into join1
           from j1 in join1.DefaultIfEmpty()
           join j2 in jq2 on x.ID equals j2.MainID into join2
           from j2 in join1.DefaultIfEmpty()
           ...
           join j6 in jq6 on x.ID equals j6.MainID into join6
           from j6 in join1.DefaultIfEmpty()
           select new {
                x.ID,
                x.Field1,
                FieldA = j1.NullOr(j=>j.FieldA),
                FieldB = j2.NullOr(j=>j.FieldB),
                ...
                FieldF = j6.NullOr(j=>j.FieldF)
              };

SQL 端现在总共需要更长的时间(更多的往返行程),但至少 Linq-To-Entities 端实际上是即时的。

向任何可以解释这种奇怪行为的人表示感谢!

于 2013-10-27T10:33:24.590 回答