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 ミリ秒未満で実行することができました。しかし、コードを実行する.ToList()と、クエリ部分が 200 ミリ秒未満であると SQL Profiler が言っているにもかかわらず、実行に30 秒以上かかっています。

ここで何が起こっているのですか?.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 に答える