EntityFramework(C#)のToList()が極端に遅くなる原因を知りたい

投稿者: Anonymous

解決したいこと

C# + Entity FrameworkアプリケーションでTake()をかまさないToList()が極端に遅い原因を知りたい。

プログラム作成中にリスト表示箇所が極端に遅かったため、VisualStudioのデバッグ機能にて問題個所を調べたところ
ToList()で極端に遅くなっていることに気づきました。

該当するソースコード


using (var db = new AppDbContext())
{
    Decimal pKey1 = XXX;
    Decimal pKey2 = XXXXX;
    Decimal pCode = XX;

    var r1 = db.V_DATA
        .Where(s => s.Key1 == pKey1)
        .Where(s => s.Key2 == pKey2)
        .Where(s => s.Code <= pCode)
        .ToList();  // ここで7,363ミリ秒(7秒程)かかる
}
============================
/// Entityクラス
public partial class V_DATA
{
    [Key]
    [Column(Order = 0, TypeName = "numeric")]
    public decimal Key1 { get; set; }

    [Key]
    [Column(Order = 1, TypeName = "numeric")]
    public decimal Key2 { get; set; }

    [Key]
    [Column(Order = 2, TypeName = "numeric")]
    public decimal Key3 { get; set; }

    [Key]
    [Column(Order = 3)]
    public string Column4 { get; set; } // 最大

    [Key]
    [Column(Order =4 , TypeName = "numeric")]
    public decimal Column5 { get; set; }

    [Key]
    [Column(Order = 5, TypeName = "numeric")]
    public decimal Column6 { get; set; }
}

ToList()で取得される件数は100件。試しにToList()直前に生成されたSQLをSSMSから実行しましたが
ほぼ0秒で結果が返ってきたためクエリチューニングの問題ではないように思えました。

var r1 = db.V_DATA
    .Where(s => s.Key1 == pKey1)
    .Where(s => s.Key2 == pKey2)
    .Where(s => s.Code <= pCode)
    .Selct(s => s); // デバッグモードで停止してSql文字列を取得

質問

以下の点についてご教示をお願い致します。

  • Take()無しで速度を向上する方法を知りたい。
  • ToList()のみで極端に遅くなる理由を知りたい。

なお、V_DATASQLServerデータベースにおいてViewとして定義されています。

検証したこと1(Take(100).ToList())⇒少し早くなる(及第点)

var r1 = db.V_DATA
    .Where(s => s.Key1 == pKey1)
    .Where(s => s.Key2 == pKey2)
    .Where(s => s.Code <= pCode)
    .Take(100)  // ToList()で取得される件数と同数の100を指定
    .ToList();  // 早くなった。⇒1,112ミリ秒(1秒程)

検証したこと2(ToArray)⇒遅い

var r1 = db.V_DATA
    .Where(s => s.Key1 == pKey1)
    .Where(s => s.Key2 == pKey2)
    .Where(s => s.Code <= pCode)
    .ToArray();  // ToArray化。同様に遅い。⇒7,576ミリ秒(7秒程)

検証したこと3(Any)⇒早い

var r1 = db.V_DATA
    .Where(s => s.Key1 == pKey1)
    .Where(s => s.Key2 == pKey2)
    .Where(s => s.Code <= pCode)
    .Any();  // Any()化。早い。⇒49ミリ秒(7秒程)

計測

別の質問サイトで指摘いただき計測しましたので、そちらのコードも載せます。

   var startTime = DateTime.Now;
    var r0 = db.V_DATA
        .Where(s => s.Key1 == pKey1)
        .Where(s => s.Key2 == pKey2)
        .Where(s => s.Code <= pCode);
    var endTime = DateTime.Now;
    Console.WriteLine ($"Where ×3 に要した時間 {endTime - startTime}");
    startTime = DateTime.Now;
    var r1 = r0.ToList (); 
    endTime = DateTime.Now;
    Console.WriteLine ($"ToList に要した時間 {endTime - startTime}");
  1. 結果↓
Where ×3 に要した時間 00:00:00.0021529
ToList に要した時間 00:00:06.9929449
  1. `Where().Take(100).ToList()とつなげた場合
Where ×1 に要した時間 00:00:00
Take(100) に要した時間 00:00:00.0032364
ToList に要した時間 00:00:00.8432526

ToListがはやくなります。。

その他

V_DATAビューは概ね以下の定義になっています。
Column4の最大桁数は11桁、その他数値項目は12桁です。

[dbo].[V_DATA](
    [Key1] [numeric](6, 0) NOT NULL,
    [Key2] [numeric](7, 0) NOT NULL,
    [Key3] [numeric](3, 0) NOT NULL,
    [Column4] [nvarchar](max) NOT NULL,
    [Column5] [numeric](22, 0) NOT NULL,
    [Column6] [numeric](22, 0) NOT NULL,
)

利用バージョンなど

  • Windows 10
  • Windows Forms アプリケーション
  • .NET Framework 4.8
  • Entity Framework 6.2.0
  • SQL Server 2014

困っています。ご回答お待ちしております。

追記 2020/10/29 16:00

SQL直接実行で早くなった

db.Database.Log = Console.Write;にて生成されたSQLを取得し、SqlQueryで実行したところ、想定外に応答が早く(170ミリ秒)なりました。原因不明です。

db.Database.SqlQuery<List<V_DATA>>(/* ここに生成されたSQL文字列 */).ToList()

解決

データベース操作のログ記録と受信でどのようなクエリが投げられているかを確認してみてください。

なお、V_DATAはSQLServerデータベースにおいてViewとして定義されています。

Viewは単なるクエリです。参照の度にクエリが展開されテーブルから読み込み直します。

SQL Serverにはインデックス付きのビューという機能もありますが、Enterprise Editionでしか結果が使われません。(クエリ オプティマイザーによる自動的なインデックス付きのビュー使用


SQL直接実行で早くなった

/// Entityクラス
public partial class V_DATA
{
    [Key]
    [Column(Order = 0, TypeName = "numeric")]
    public decimal Key1 { get; set; }

Entityクラスのすべてのメンバーに[Key]が付けられています。これは適切でしょうか? 主キーでないものには付けるべきではありません。


また、追跡なしのクエリとして.AsNoTracking()を追加することができます。これを行うとオブジェクト状態が追跡されなくなるためオブジェクト生成が高速化されます。ただし、オブジェクトの値を更新後にDBへ保存できなくなります。

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *