web-dev-qa-db-ja.com

Entity Frameworkのコア数に最適なパフォーマンスがありません

特定のフィルターでレコードの量を取得する必要があります。

理論的にこの命令:

_dbContext.People.Count (w => w.Type == 1);

次のようなSQLを生成する必要があります。

Select count (*)
from People
Where Type = 1

ただし、生成されるSQLは次のとおりです。

Select Id, Name, Type, DateCreated, DateLastUpdate, Address
from People
Where Type = 1

生成されるクエリは、多くのレコードを持つデータベースで実行するのに非常に時間がかかります。

最初のクエリを生成する必要があります。

私がこれをするだけなら:

_dbContext.People.Count ();

Entity Frameworkは次のクエリを生成します。

Select count (*)
from People

..これは非常に高速に実行されます。

検索条件をカウントに渡すこの2番目のクエリを生成する方法は?

17
Renatto Machado

ここで答えるべきことはあまりありません。 ORMツールが単純なLINQクエリから期待されるSQLクエリを生成しない場合、クエリを書き直してそれを実行する方法はありません(最初にそれを行うべきではありません)。

EF Coreには、LINQクエリでのクライアント/データベース評価の混合という概念があります.

EF Coreにない機能 (Wordnot)と Roadmap からの抜粋:

より多くのクエリを正常に実行できるように翻訳を改善し、メモリ内ではなくデータベースでより多くのロジックを評価します。

まもなく、クエリ処理の改善を計画していますが、それがいつ起こるか、どの程度のレベルかはわかりません(混合モードではクエリが「機能している」と見なすことができます)。

それでは、オプションは何ですか?

  • まず、EF Coreが本当に役立つようになるまでEF Coreに近づかないでください。 EF6に戻ると、このような問題はありません。
  • EF6を使用できない場合は、最新のEF Coreバージョンで更新されたままにしてください。

たとえば、v1.0.1とv1.1.0の両方でクエリを実行すると、目的のSQL(テスト済み)が生成されるため、単純にアップグレードでき、具体的な問題はなくなります。

しかし、改善とともに新しいリリースではバグ/リグレッションが発生することに注意してください(ここで見られるように たとえば、EFCoreは単純なLEFT OUTER結合に対して多すぎる列を返します )。最初のオプション、つまり どちらが適切か :)をもう一度考えてください

12
Ivan Stoev

このラムダ式を使用して、クエリをより速く実行してみてください。

_dbContext.People.select(x=> x.id).Count();
6
Raju Mali

これを試して

(from x in _dbContext.People where x.Type == 1 select x).Count();

または、次のように非同期バージョンを実行できます。

await (from x in _dbContext.People where x.Type == 1 select x).CountAsync();

そして、それらがあなたのためにうまくいかないなら、あなたは少なくとも以下を行うことでクエリをより効率的にすることができます:

(from x in _dbContext.People where x.Type == 1 select x.Id).Count();

または

await (from x in _dbContext.People where x.Type == 1 select x.Id).CountAsync();
4
Patrick Mcvay

パフォーマンスを最適化したいが、現在のEFプロバイダーが(まだ)目的のクエリを生成できない場合、常に raw SQL に依存できます。

明らかに、これはEFを使用してSQLを直接記述しないようにするためのトレードオフですが、実行するクエリがLINQを使用して表現できない場合、またはLINQクエリを使用して結果が得られる場合は、生のSQLを使用すると便利ですデータベースに送信される非効率的なSQL。

サンプルの未加工SQLクエリは次のようになります。

var results = _context.People.FromSql("SELECT Id, Name, Type, " +
                                      "FROM People " +
                                      "WHERE Type = @p0",                                                     
                                      1);

私の知る限り、FromSql拡張メソッドに渡される生のSQLクエリでは、現在、モデルタイプを返す必要があります。つまり、スカラー結果を返すことはまだサポートされていない可能性があります。

ただし、いつでもプレーンなADO.NETクエリに戻ることができます。

using (var connection = _context.Database.GetDbConnection())
{
    connection.Open();

    using (var command = connection.CreateCommand())
    {
        command.CommandText = "SELECT COUNT(*) FROM People WHERE Type = 1";
        var result = command.ExecuteScalar().ToString();
    }
}
3
Dirk Vollmar

Entity Framework Coreの初期リリースの1つに何らかの問題があったようです。残念ながら、正確なバージョンを指定していないので、EFソースコードを掘り下げて、何が間違っているのかを正確に伝えることはできません。

このシナリオをテストするために、最新のEF Coreパッケージをインストールし、正しい結果を得ることができました。

ここに私のテストプログラムがあります: Source code

そして、SQL Server ProfilerでキャプチャされたSQLが生成されます: enter image description here

ご覧のとおり、すべての期待に一致しています。

Packages.configファイルからの抜粋を次に示します。

...
<package id="Microsoft.EntityFrameworkCore" version="1.1.0" targetFramework="net452" />
...

したがって、あなたの状況で唯一の解決策は、これを書いている時点で1.1.0である最新のパッケージに更新することです。

3
Kaspars Ozols

これはあなたが欲しいものを手に入れますか:

_dbContext.People.Where(w => w.Type == 1).Count();
2
Theo

バンプでごめんなさい、しかし...

おそらく、where句を含むクエリが遅い理由は、データベースに高速な実行方法を提供しなかったためです。

peopleクエリからのselect count(*)の場合、各フィールドの実際のデータを知る必要はなく、これらのフィールドがすべて含まれていない小さなインデックスを使用するだけなので、遅いI/Oを費やす必要はありません。オン。データベースソフトウェアは、プライマリキーインデックスが必要とするI/Oが最も少ないことを確認するのに十分賢いでしょう。 pk idは行全体よりも必要なスペースが少ないため、I/Oブロックごとにカウントを戻すことができるため、より速く完了できます。

ここで、Typeを含むクエリの場合、Typeを読み取って値を決定する必要があります。クエリを高速にしたい場合は、Typeにインデックスを作成する必要があります。そうしないと、非常に遅い全テーブルスキャンを実行してすべての行を読み取る必要があります。あなたの価値観がより差別的であるときに役立ちます。列の性別(通常)には2つの値のみがあり、あまり差別的ではありません。すべての値が一意である主キー列は、非常に差別的です。値を大きくすると、インデックス範囲のスキャンが短くなり、カウント結果が速くなります。

ここではEFCore 1.1を使用しています。

これは、EFCoreがWhere句全体をSQLに変換できない場合に発生する可能性があります。これは_DateTime.Now_のような単純なものであり、考えさえしないかもしれません。

次のステートメントは、テーブル全体をロードすると、驚くほど_SELECT *_を実行し、C#.Count()を実行するSQLクエリになります!

_   int sentCount = ctx.ScheduledEmail.Where(x => x.template == template &&
                   x.SendConfirmedDate > DateTime.Now.AddDays(-7)).Count();
_

しかし、このクエリはSQL SELECT COUNT(*)を実行します:

_   DateTime earliestDate = DateTime.Now.AddDays(-7);
   int sentCount = ctx.ScheduledEmail.Where(x => x.template == template 
                   && x.SendConfirmedDate > earliestDate).Count();
_

クレイジーだが真実。幸い、これも機能します。

_   DateTime now = DateTime.Now;
   int sentCount = ctx.ScheduledEmail.Where(x => x.template == template &&
                   x.SendConfirmedDate > now.AddDays(-7)).Count();
_
1
Simon_Weaver