2

3つのテーブル(Worker、Task、TaskStep)でデータベースを使用するプログラムを構築しています。タスクの特定のワーカーの日付を取得してレポートを作成し、特定の日のステップを実行するメソッドがあります。

データベースの構造は次のとおりです。

MySQL 5.2

Workerテーブル列:

workerID(VARCHAR(45)),
name(VARCHAR(45)),
age(int),
...

Tasksテーブル列:

TaskID(VARCHAR(45)),
description(VARCHAR(45)),
date(DATE),
...

TaskStepsテーブル列:

TaskStepID(VARCHAR(45)),
description(VARCHAR(45)),
date(DATE),
...

どのテーブルにもインデックスはありません

問題はそれが非常に遅いということです!! (〜20秒)

コードは次のとおりです。

using WorkerDailyReport = Dictionary<task, IEnumerable<taskStep>>;

private void Buildreport(DateTime date)
{
    var report = new WorkerDailyReport();    

    // Load from DB
    var sw = new Stopwatch();
    sw.Start();

    var startOfDay  = date.Date;
    var endOfDay    = startOfDay.AddDays(1);
    var db          = new WorkEntities();

    const string    workerID   = "80900855";

    IEnumerable<task> _tasks = db.task
                    .Where(ta =>    ta.date     >= startOfDay   &&
                                    ta.date     <  endOfDay     &&
                                    ta.workerID == workerID)
                    .ToList();

    sw.Stop();
    Console.WriteLine("Load From DB time - " + sw.Elapsed + 
                      ", Count - "           + _tasks.Count());   

    // Build the report
    sw.Restart();

    foreach (var t in _tasks)
    {
        var ts = db.taskStep.Where(s => s.taskID == task.taskID);

        report.Add(t, ts);
    }

    sw.Stop();
    Console.WriteLine("Build report time - " + sw.Elapsed);

    // Do somthing with the report
    foreach (var t in report)
    {
        sw.Restart();

        foreach (var subNode in t.Value)
        {
            // Do somthing..
        }

        Console.WriteLine("Do somthing time - " + sw.Elapsed + 
                          ", Count - " + t.Value.Count());
    }
}

あなたが見ることができるように、私はストップウォッチを各部分に置いて、何がそんなに時間がかかるかをチェックします、そしてこれは結果です:

1)

上記のようにコードを実行した場合:

コンソール:

Load From DB time - 00:00:00.0013774, Count - 577

Build report time - 00:00:03.6305722

Do somthing time - 00:00:07.7573754, Count - 21

Do somthing time - 00:00:08.2811928, Count - 11

Do somthing time - 00:00:07.8715531, Count - 14

Do somthing time - 00:00:08.0430597, Count - 0

Do somthing time - 00:00:07.7867790, Count - 9

Do somthing time - 00:00:07.3485209, Count - 39

.........

内側のforeach実行には約7〜9かかります!! 40レコードを超えないようにするための秒。

2)

1つだけ変更する場合は、データベースからワーカータスクをロードするときに、最初のクエリの後に.ToList()を追加すると、すべて変更されます。

コンソール:

Load From DB time - 00:00:04.3568445, Count - 577

Build report time - 00:00:00.0018535

Do somthing time - 00:00:00.0191099, Count - 21

Do somthing time - 00:00:00.0144895, Count - 11

Do somthing time - 00:00:00.0150208, Count - 14

Do somthing time - 00:00:00.0179021, Count - 0

Do somthing time - 00:00:00.0151372, Count - 9

Do somthing time - 00:00:00.0155703, Count - 39

.........

現在、データベースからのロードにはさらに多くの時間がかかり、4秒以上かかります。ただし、ビルドされたレポート時間は約1ミリ秒で、各内部foreachには約10ミリ秒かかります

最初の方法は不可能であり(577 *〜8秒)、2番目のオプションも非常に遅く、yが表示されません。

ここで何が起こっているのか分かりますか?

1)なぜToList()そんなに遅いのですか?

2)なぜToList()、内部foreachレポートとビルドレポートが遅くなっているのですか?

どうすれば速くできますか?

thnx。

4

3 に答える 3

2

.ToList() を使用しない場合、C# はデータベースからデータを取得する必要があるまでデータベースからデータをロードしません。これは、エンティティ フレームワークでの遅延読み込みが原因です。

内側の for-each ループのすべてのステップで、プログラムはデータベースからクエリを要求しますが、これは非常に遅いです。

ただし、.ToList() を使用すると、クエリをすぐに実行して最初にすべてのレコードを取得するため、処理が遅くなります。次に、内側の for-each ループで、プログラムはすべてのレコードをメモリに保持します。

英語を話すのが下手ですみません :D

于 2012-12-25T16:52:22.457 に答える
0

パフォーマンスを向上させるには、1 つのクエリを使用してすべてのテーブルからデータを取得する必要があります。

var _joinedTasks = db.task.Where(ta =>    ta.date     >= startOfDay   &&
                                    ta.date     <  endOfDay     &&
                                    ta.workerID == workerID)
                    .Join(db.taskStep, t => t.taskID, ts=>ts.taskID, (t, ts) => new {t, ts})
                    .GroupBy(g => g.t, v=>v.ts).AsEnumerable();

次に、それを辞書に追加できます。

var report = _joinedTasks.ToDictionary(g=>g.Key);

そして、このレポートを自由に使用してください。

于 2012-12-26T05:56:33.367 に答える
0

LINQToList()は常にシーケンスをすぐに評価します。この場合、データベースに対する SQL クエリです。

あなたが得た最初のインスタンスLoad From DB time - 00:00:00.0013774, Count - 577では、SQLクエリを実行しなかったので速かったです。ただし、クエリは少し後で実行されました-それがBuild report time - 00:00:03.6305722(遅い) になった理由です。

2番目のインスタンスでは、クエリの強制評価をすぐに追加ToList()します(SQLを実行します)。そのため、次の時間が得られます。

  • Load From DB time - 00:00:04.3568445, Count - 577- データベースに対する SQL クエリ (遅い)
  • Build report time - 00:00:00.0018535- すでにメモリ内にあるデータの操作 (高速)

興味深いのは、 577個のアイテムを返すクエリに3 秒以上かかったという事実です。これは、テーブルの 1 つでインデックスが欠落しているために発生する可能性があります。

テーブルにインデックスがない場合、データベース システムはフル テーブル スキャンを実行して、次の条件を満たすすべての項目を見つける必要があることに注意してください。

.Where(ta => ta.date >= startOfDay &&
             ta.date < endOfDay &&
             ta.workerID == workerID)

テーブル内のアイテム数が増えるTasks、クエリにかかる時間がますます長くなります。

したがって、Tasks.dateおよびTasks.workerId列にインデックスを作成することを強くお勧めします。これにより、最初のクエリ時間が改善されるはずです (データベース接続が高速であると仮定します。つまり、海を越えて展開されたデータベースに接続していないことを前提としています)。

ところで、すべてのテーブル列にインデックスを作成しないでください (クエリ条件で使用する列のみ)。これにより、挿入操作が遅くなり、データベースのサイズが大きくなる可能性があります。

残念ながらDo somthing time ...、コードを提供していないため、これ以上のアドバイスはできません。しかし、同じアドバイスを適用すれば、あなたにもいくつかの改善が得られると確信しています.

于 2012-12-26T01:57:53.697 に答える