0

ExecuteReader()コマンドを使用して、いくつかのテーブルを含むデータベースを読み取っています。最初のRead()の結果に基づいて、2番目のクエリを実行するために最初のクエリで返されたIDが必要なため、2つの異なるテーブルを読み取りました。

問題は、この検索が非常に遅いことです。

tuCommand.CommandText = "SELECT * FROM tblTranslationUnit WHERE DocumentId = " + doc.DocumentId;
var tuReader = tuCommand.ExecuteReader();
while (tuReader.Read())
{
    var tu = new TranslationUnit
     {
         TranslationUnitId = tuReader.GetInt64(0),
         DocumentId = tuReader.GetInt64(1),
         Raw = tuReader.GetString(2),
         IsSegmented = tuReader.GetBoolean(3),
         Reader = this, // Ryan: Fixed so that it sets the reader to itself
     };

    using (var propCommand = _dbConn.CreateCommand())
    {
        propCommand.CommandText = "SELECT * FROM tblTranslationUnitProperties WHERE TranslationUnitId = " + tu.TranslationUnitId;
        var propReader = propCommand.ExecuteReader();
        while (propReader.Read()) tu.Properties.Add(GetProperty(propReader));
    }
    yield return tu;
}

2番目のExecuteReader()を削除すると、クエリは非常に高速になります

また、新しい接続と新しいトランザクションを使用して2番目のExecuteReader()を配置しようとしましたが、結果はほぼ同じです。

何かアイデアや手がかりはありますか?この種の検索を行うにはどうすればよいですか?より良いアプローチはありますか?(私はそうだと思います)。


詳細db構造は次のとおりです。

  - Document
      - properties
      - errors
    -TranslationUnits
        - properties
        - errors
      - Segments
          - properties
          - errors

したがって、コードの一部では、この構造になります

  foreach (document in db)
      foreach (property in document)
      foreach (error in document)
    foreach (translationunit in document)
        foreach (property in translationunit)
        foreach (error in translationunit)
      foreach (segment in translationunit)
          foreach (property in segment)
          foreach (error in segment)

それに基づいて、すべてを返すために結合を使用することは、問題が単なるSQLite構成の問題であるかどうかを考えていたのは良い考えではありません。つまり、パラメータなどを追加して、複数のポインタを使用することをシステムに通知できる場合です。

現在、データテーブルソリューションに移行しています。

  1. 接続を開く
  2. テーブルの1000エントリを読み取る
  3. 接続を閉じます
  4. 新しい接続を開く
  5. 子テーブルの1000エントリを読み取ります
  6. 新しい接続を閉じます
  7. ..。
4

5 に答える 5

1

こんにちは私はこれに私の発見を追加するつもりです(私はデビッドと一緒に働いています)

davidが説明したように、バッファを使用してデータベースからテーブルを読み取る方法を変更したため、同時接続やリーダーが同時に実行されることはありません。少し速いようですが、目立ちません。ここにいくつかの数字があります。

データベース(すべてのテーブル)に、2.5秒で5000の変換ユニットを入力します。次に、TranslationUnitテーブル(約5000行)をループすると、読み取り時間は見事です:0.07秒。コードは次のようになります:

foreach (var tu in document)
{
   ... do something ...
}

このように各翻訳ユニットのセグメントを読んだ場合:

foreach (var tu in document)
{
    foreach (var seg in tu)
    {
        ... do something ...
    }
}

読書時間は醜くなり始めます:約10秒。各翻訳ユニットには正確に2つのセグメントがあることに注意してください(ただし、これは設計で制限されていません)。

10000トランスレーションユニットの場合、データベースにデータを入力するのに約6秒、データベースを読み取るのに約2分かかります。(翻訳単位を読み取るforeachが1つしかない場合は、ほぼ瞬時に)

50000の翻訳単位の場合、入力に約32秒かかり、読み取りが完了するのを1時間待った後、あきらめました。(翻訳単位を読み取るforeachが1つしかない場合は、ほぼ瞬時に)

ですから、私の推測では、読書時間のコストは指数関数的に増加しています。これは、データベースポインタを別のテーブルに変更する必要があるためだと考えるのが妥当でしょうか。(変換ユニットとセグメントテーブルの間)。

于 2012-04-21T22:37:19.780 に答える
1

スケーラビリティの問題があるようです。SQLite には、理由から「Lite」という単語が含まれています。高い同時実行性、きめの細かいアクセス制御、組み込み関数の豊富なセット、ストアド プロシージャ、難解な SQL 言語機能、XML や Java の拡張機能、テラバイトまたはペタバイトのスケーラビリティなど、特定の機能が欠けています。まず、データベースを変更することをお勧めします。

また、あなたの質問では、一度に 1000 個のドキュメント、特に 1000 個のパーツとさらに 1000 個のパーツを含む 1000 個のドキュメントがすべてメモリ内に必要な理由がわかりませ。あなたのUI要件はわかりませんが、15年以上のプログラミングで、何らかのページングメカニズムなしで単一のWebページまたはフォームに1000を表示する必要があることを思い出したことはありません。データベースから 1000 * 1000 * 1000 個のエンティティを一度に?

UI、現在のモデル、およびデータ レイヤーをもう一度見て、パフォーマンスを大幅に犠牲にすることなく必要なだけコンテンツを配信する方法を探す必要があると思います。初期費用を削減するために、遅延読み込み、先読みバッファ、キャッシュ、ページング、検索方法、共有静的データなどのパターンの使用を検討してください。

家を買うという観点から考えてみましょう。私たちのほとんどは、家を前払いするためのお金を持っていないので、住宅ローンを組んでいます。住宅ローンは、初期費用を時間の経過とともに分散させる方法です。すべての住宅ローンには、利子と呼ばれるマイナスの影響があります。今では、100,000 を支払う代わりに、全体のコストは 250,000 になりますが、現在の支払いを支払う余裕があるため、余分なコストは時間の経過とともに少しずつ吸収されるため、余分な 150,000 に実際には気づきません。また、ローンを完済する代わりに 5 年で家を売却した場合、250,000 の全額を返済することさえできない可能性があることにも注意してください。

ここでのポイントは、より小さなレコードセットを取得するために余分な接続を作成するコストを分散し、ユーザーが現在必要としているものを提供できるということです。これにより、全体的な先行コストが削減されますが、取得される個々のレコードセットに追加のコストが追加されます。

于 2012-04-19T11:21:49.027 に答える
0

まず、select を join で記述し、1 つのクエリですべてを取得できます

SELECT * FROM tblTranslationUnit join tblTranslationUnitProperties on
tblTranslationUnitProperties.TranslationUnitId = tblTranslationUnit.id 
WHERE DocumentId = @docID //<= use parameter

役に立たない場合は、テーブルにインデックスを付ける必要があるかもしれません。

于 2012-04-19T11:12:39.620 に答える
0

簡単な「JOIN」は試しましたか?または、あなたの質問に何か欠けていますか?

SELECT tbl2.* 
    FROM tblTranslationUnit tbl1 
    JOIN tblTranslationUnitProperties tbl2 ON tbl2.TranslationUnitId = tbl1.TranslationUnitId 
于 2012-04-19T11:09:59.373 に答える
0

最初のクエリからのすべての結果を一度に読み取り、 を閉じてDataReaderから、メモリ内の結果を列挙します。

于 2012-04-19T11:16:01.383 に答える