22

Entity Framework がネストされた SQL クエリを生成するのはなぜですか?

私はこのコードを持っています

    var db = new Context();
    var result = db.Network.Where(x => x.ServerID == serverId)
        .OrderBy(x=> x.StartTime)
        .Take(limit);

これを生成するもの!(二重選択ステートメントに注意してください)

SELECT
`Project1`.`Id`, 
`Project1`.`ServerID`, 
`Project1`.`EventId`, 
`Project1`.`StartTime`
FROM (SELECT
`Extent1`.`Id`, 
`Extent1`.`ServerID`, 
`Extent1`.`EventId`, 
`Extent1`.`StartTime`
FROM `Networkes` AS `Extent1`
 WHERE `Extent1`.`ServerID` = @p__linq__0) AS `Project1`
 ORDER BY 
`Project1`.`StartTime` DESC LIMIT 5

1 つの select ステートメントになるようにするには、何を変更すればよいですか? Code First で MySQL と Entity Framework を使用しています。

アップデート

メソッドに渡されるパラメーターのタイプに関係なく、同じ結果が得られますOrderBy()

更新 2: 時限

Total Time (hh:mm:ss.ms)    05:34:13.000
Average Time (hh:mm:ss.ms)  25:42.000
Max Time (hh:mm:ss.ms)  51:54.000
Count   13
First Seen  Nov 6, 12 19:48:19
Last Seen   Nov 6, 12 20:40:22

生のクエリ:

SELECT `Project?`.`Id`, `Project?`.`ServerID`, `Project?`.`EventId`, `Project?`.`StartTime` FROM (SELECT `Extent?`.`Id`, `Extent?`.`ServerID`, `Extent?`.`EventId`, `Extent?`.`StartTime`, FROM `Network` AS `Extent?` WHERE `Extent?`.`ServerID` = ?) AS `Project?` ORDER BY `Project?`.`Starttime` DESC LIMIT ?

プログラムを使用して、MySQL の現在のプロセスからスナップショットを取得しました。

他のクエリは同時に実行されましたが、1 つの SELECT ステートメントに変更すると、1 秒を超えることはありません。多分私は何か他のことが起こっています。私はDBにあまり興味がないので聞いています...

更新 3: Explain ステートメント

生成された Entity Framework

'1', 'PRIMARY', '<derived2>', 'ALL', NULL, NULL, NULL, NULL, '46', 'Using filesort'
'2', 'DERIVED', 'Extent?', 'ref', 'serveridneventid,serverid', 'serveridneventid', '109', '', '45', 'Using where'

一発ギャグ

'1', 'SIMPLE', 'network', 'ref', 'serveridneventid,serverid', 'serveridneventid', '109', 'const', '45', 'Using where; Using filesort'

これは私の QA 環境からのものなので、上に貼り付けたタイミングは行数の Explain ステートメントとは関係ありません。1 つのサーバー ID に一致するレコードは 50 万件程度あると思います。

解決

MySQL から SQL Server に切り替えました。アプリケーション層を完全に書き直すことはしたくありません。

4

6 に答える 6

8

これは、式ツリーから論理的にクエリを作成する最も簡単な方法です。通常、パフォーマンスは問題になりません。パフォーマンスの問題がある場合は、エンティティを元に戻すために次のようなことを試すことができます。

var results = db.ExecuteStoreQuery<Network>(
    "SELECT Id, ServerID, EventId, StartTime FROM Network WHERE ServerID = @ID", 
    serverId);

results = results.OrderBy(x=> x.StartTime).Take(limit);
于 2012-11-06T20:24:18.610 に答える
3

私の最初の印象は、この方法で行う方が実際には効率的であるということでしたが、MSSQLサーバーに対するテストでは、1秒未満の応答が返されました。

単一のselectステートメントで、すべてのレコードをソートし(Order By)、次にそれらを表示したいセットにフィルター処理し(Where)、次に上位5つ(Limit 5または、私にとってはTop 5)を取得します。大きなテーブルでは、ソートにかなりの時間がかかります。ネストされたステートメントを使用すると、最初にレコードをサブセットにフィルターしてから、コストのかかるソート操作を実行します。

編集:私はこれをテストしましたが、テストでエラーが発生し、それが無効になっていることに気付きました。テスト結果は削除されました。

于 2012-11-06T19:30:28.097 に答える
2

Entity Framework が入れ子になったクエリを生成するのはなぜですか? 簡単な答えは、Entity Framework がクエリ式を式ツリーに分解し、その式ツリーを使用してクエリを作成するためです。ツリーは、ネストされたクエリ式を自然に生成します (つまり、子ノードがクエリを生成し、親ノードがそのクエリに対するクエリを生成します)。

Entity Framework がクエリを単純化して、ユーザーのように記述しないのはなぜですか? 簡単な答えは、クエリ生成エンジンに投入できる作業の量が限られているためです。現在は以前のバージョンよりも優れていますが、完全ではなく、おそらく今後もそうはならないでしょう。

手動で記述したクエリと、この場合に生成されたクエリ EF との間に大きな速度の違いはないはずです。データベースは、どちらの場合でも最初に WHERE 句を適用する実行計画を生成するのに十分賢いです。

于 2012-11-06T19:49:50.820 に答える
1

私は同じ問題に苦しんでいるので、この投稿に出くわしました。私はすでにこれを追跡するのに何日も費やしていますが、それはmysqlでのクエリ生成が不十分です。

私はすでに mysql.com http://bugs.mysql.com/bug.php?id=75272でバグを報告しました

問題を要約すると、次のようになります。

この単純なクエリ

context.products
    .Include(x => x.category)
    .Take(10)
    .ToList();

に翻訳されます

SELECT
`Limit1`.`C1`, 
`Limit1`.`id`, 
`Limit1`.`name`, 
`Limit1`.`category_id`, 
`Limit1`.`id1`, 
`Limit1`.`name1`
FROM (SELECT
`Extent1`.`id`, 
`Extent1`.`name`, 
`Extent1`.`category_id`, 
`Extent2`.`id` AS `id1`, 
`Extent2`.`name` AS `name1`, 
1 AS `C1`
FROM `products` AS `Extent1` INNER JOIN `categories` AS `Extent2` ON `Extent1`.`category_id` = `Extent2`.`id` LIMIT 10) AS `Limit1`

そしてかなりうまく機能します。とにかく、外側のクエリはほとんど役に立ちません。ここで OrderBy を追加すると

context.products
    .Include(x => x.category)
    .OrderBy(x => x.id)
    .Take(10)
    .ToList();

クエリは次のように変わります

SELECT
`Project1`.`C1`, 
`Project1`.`id`, 
`Project1`.`name`, 
`Project1`.`category_id`, 
`Project1`.`id1`, 
`Project1`.`name1`
FROM (SELECT
`Extent1`.`id`, 
`Extent1`.`name`, 
`Extent1`.`category_id`, 
`Extent2`.`id` AS `id1`, 
`Extent2`.`name` AS `name1`, 
1 AS `C1`
FROM `products` AS `Extent1` INNER JOIN `categories` AS `Extent2` ON `Extent1`.`category_id` = `Extent2`.`id`) AS `Project1`
 ORDER BY 
`Project1`.`id` ASC LIMIT 10

order byが外側のクエリにあるため、これは悪いことです。Theat は、MySQL が orderby を実行するためにすべてのレコードをプルする必要があることを意味します。using filesort

SQL Server (少なくとも Comapact) が同じコードに対してネストされたクエリを生成しないことを確認しました

SELECT TOP (10) 
[Extent1].[id] AS [id], 
[Extent1].[name] AS [name], 
[Extent1].[category_id] AS [category_id], 
[Extent2].[id] AS [id1], 
[Extent2].[name] AS [name1], 
FROM  [products] AS [Extent1]
LEFT OUTER JOIN [categories] AS [Extent2] ON [Extent1].[category_id] = [Extent2].[id]
ORDER BY [Extent1].[id] ASC
于 2015-01-13T15:09:30.050 に答える
1

副選択なしで EF を取得してクエリを生成する場合は、クエリ内で変数ではなく定数を使用します。

以前に、独自の .Where と、最初に式ツリーを走査し、すべての変数、メソッド呼び出しなどを Expression.Constant に変換する他のすべての LINQ メソッドを作成しました。Entity Frameworkのこの問題のためだけに行われました...

于 2012-11-06T19:27:30.040 に答える
-2

実際、Entity Frameworkによって生成されるクエリは、醜いものはほとんどなく、LINQ 2 SQLよりも少ないですが、それでも醜いものです。

ただし、データベースエンジンが目的の実行プランを作成し、クエリがスムーズに実行される可能性があります。

于 2012-11-06T19:29:42.933 に答える