web-dev-qa-db-ja.com

エンティティへのLinq-すべてのVS最初のVSが存在します

Entity Frameworkを使用していますが、名前が「xyz」の製品が存在するかどうかを確認する必要があります...

Any()、Exists()、First()を使用できると思います。

このような状況に最適なオプションはどれですか。どれが最高のパフォーマンスを持っていますか?

ありがとうございました、

ミゲル

15
Miguel Moura

Anyは、データベースレベルで「存在」に変換されます。最初はSelect Top 1に変換されます...これらの間では、Existsが最初に実行されます。これは、実際のオブジェクトをフェッチする必要がなく、ブール値の結果値のみが必要なためです。

少なくとも、.Where(x => x.Count()> 0)について質問しなかったため、1つのレコードがあると判断する前に、一致セット全体を評価して繰り返す必要があります。要求が短絡され、大幅に速くなる可能性があります。

21
Jim Wooley

さて、私はこれについて検討するつもりはありませんでしたが、ディエゴの答えは事態を複雑にしているので、追加の説明が必要だと思います。

ほとんどの場合、.Any()の方が高速です。以下にいくつかの例を示します。

_Workflows.Where(w => w.Activities.Any())
Workflows.Where(w => w.Activities.Any(a => a.Title == "xyz"))
_

上記の2つの例では、Entity Frameworkは最適なクエリを生成します。 .Any()呼び出しは述語の一部であり、Entity Frameworkはこれを適切に処理します。ただし、次のように.Any()の結果を結果セットの一部にすると、

_Workflows.Select(w => w.Activities.Any(a => a.Title == "xyz"))
_

...突然Entity Frameworkは2つのバージョンの条件を作成することを決定するため、クエリは実際に必要な作業の2倍もの処理を実行します。ただし、次のクエリの方が優れています。

_Workflows.Select(w => w.Activities.Count(a => a.Title == "xyz") > 0)
_

上記のクエリを前提として、Entity Frameworkは引き続き2つのバージョンの条件を作成します。さらに、SQL Serverが実際のカウントを行う必要があります。つまり、アイテムが見つかるとすぐに短絡することはありません。

しかし、これら2つのクエリを比較しているだけの場合:

  1. Activities.Any(a => a.Title == "xyz")
  2. Activities.Count(a => a.Title == "xyz") > 0

...どちらが速くなりますか?場合によります。

最初のクエリは非効率的な二重条件クエリを生成します。つまり、必要な時間の最大2倍の時間がかかります。

2番目のクエリは、データベースにテーブル内のすべてのアイテムを短絡させずにチェックするように強制します。つまり、一致を見つける前に評価する必要があるアイテムの数によっては、必要な時間よりも最大N時間がかかる可能性があります。テーブルに10,000のアイテムがあると仮定します。

  • テーブルのどの項目も条件に一致しない場合、このクエリは最初のクエリとしてほぼ半分の時間を使用します。
  • テーブルの最初の項目が条件に一致する場合、このクエリは最初のクエリより約5,000倍長くかかります。
  • テーブル内の1つの項目が一致する場合、このクエリは最初のクエリよりも平均2,500倍長い時間になります。
  • クエリがTitleとキー列のインデックスを利用できる場合、このクエリは最初のクエリとしてほぼ半分の時間を使用します。

つまり、要約すると、

  1. 使用する Entity Framework 4(新しいバージョンではクエリ構造が改善される可能性があるため) Entity Framework 6.1以前( 6.1.1にはクエリを改善するための修正があるため )、かつ
  2. (サブクエリを実行するのではなく)テーブルに対して直接クエリを実行し、かつ
  3. (述語の一部ではなく)結果を直接使用し、かつ
  4. どちらか:
    1. クエリしているテーブルに適切なインデックスが設定されている、または
    2. ほとんどの場合、アイテムが見つからないことを期待している

次に、.Any().Count()の2倍の時間がかかると予想できます。たとえば、クエリは50ミリ秒ではなく100ミリ秒かかる場合があります。または、5ミリ秒ではなく10ミリ秒です。

その他の状況.Any()少なくとも同じくらい速くである必要があります桁より速い.Count()

関係なく、これが実際に製品のパフォーマンスの低下の原因であると判断するまでは、理解しやすいことについてさらに注意する必要があります。 .Any()は、あなたが本当に理解しようとしていることをより明確かつ簡潔に述べているので、それに固執してください。

28

Any()EXISTSクエリに変換されるため、より良い結果が得られると思いますが、EFはひどく壊れており、これを生成します(編集):

_SELECT 
CASE WHEN ( EXISTS (SELECT 
    1 AS [C1]
    FROM [MyTable] AS [Extent1]
    WHERE Condition
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT 
    1 AS [C1]
    FROM [MyTable] AS [Extent2]
    WHERE Condition
)) THEN cast(0 as bit) END AS [C1]
FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]
_

の代わりに:

_SELECT 
CASE WHEN ( EXISTS (SELECT 
    1 AS [C1]
    FROM [MyTable] AS [Extent1]
    WHERE Condition
)) THEN cast(1 as bit)
   ELSE cast(0 as bit) END AS [C1]
FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]
_

...基本的にクエリコストが2倍になります(単純なクエリの場合、複雑なクエリの場合はさらに悪くなります)。

私は.Count(condition) > 0を使用すると、常にかなり高速になります(コストは、適切に作成されたEXISTSクエリとまったく同じです)。

2
Diego Mijelshon

Any()およびFirst()IEnumerableと共に使用され、物事を遅延評価する柔軟性を提供します。ただし、Exists()にはListが必要です。

これで問題が解決し、どちらを使用するかを決定するときに役立ちます。

0
Tabish Sarwar