7

TADOQueryから読み取ったパフォーマンスの修正を見つけようとしています。現在、Q.eof do begin ... Q.nextメソッドではなく、'を使用してレコードをループします。それぞれについて、各レコードのIDと値を読み取り、それぞれをコンボボックスリストに追加します。

指定したフィールドのすべての値を一度にリストに変換する方法はありますか?データセットをループするのではなく?こんなことができたら本当に重宝します…

TStrings(MyList).Assign(Q.ValuesOfField['Val']);

それが本当のコマンドではないことは知っていますが、それが私が探しているコンセプトです。迅速な対応と解決策を探しています(いつものように、これは本当に緊急のパフォーマンスの問題を修正するためです)。

4

8 に答える 8

13

あなたのコメントを見て、ここにいくつかの提案があります:

この状況でボトルネックになる可能性のあることがいくつかあります。1つ目は、フィールドを繰り返し検索することです。呼び出している場合、FieldByNameまたはFindFieldループ内にいる場合は、変更されない値を再計算するためにCPU時間を浪費していることになります。読み取り元のフィールドごとにFieldByNameを1回呼び出し、代わりにローカル変数に割り当てます。

フィールドから値を取得するときは、AsStringまたはAsInteger、または探しているデータ型を返す他のメソッドを呼び出します。プロパティから読んでいる場合は、コンバージョンTField.Valueに時間を浪費しています。variant

Delphiコンボボックスに多数のアイテムを追加する場合は、おそらくItemsプロパティの形式の文字列リストを処理していることになります。リストのプロパティを設定し、更新を開始する前にCapacity必ず呼び出し、最後に呼び出してください。これにより、大量のデータの読み込みを高速化する内部最適化が可能になります。BeginUpdateEndUpdate

使用しているコンボボックスによっては、内部リスト内の多数のアイテムを処理する際に問題が発生する可能性があります。「仮想」モードがあるかどうかを確認します。このモードでは、すべてを事前に読み込むのではなく、必要なアイテムの数を指定するだけで、ドロップダウンすると、表示されるはずのアイテムごとにイベントハンドラーが呼び出されます。画面に表示し、表示する適切なテキストを指定します。これにより、特定のUIコントロールを実際に高速化できます。

また、もちろん、データベースクエリ自体が高速であることを確認する必要がありますが、SQLの最適化はこの質問の範囲を超えています。

そして最後に、Mikael Erikssonのコメントは間違いなく注目に値します!

于 2011-11-04T23:19:10.200 に答える
9

Getrowsを使用できます。関心のある列を指定すると、値を含む配列が返されます。コンボ ボックスに 22.000 行を追加するのにかかる時間は、while not ADOQuery1.Eof ...ループの場合は 7 秒でしたが、私のテストでは 1.3 秒になりました。

サンプルコード:

var
  V: Variant;
  I: Integer;
begin
  V := ADOQuery1.Recordset.GetRows(adGetRowsRest, EmptyParam, 'ColumnName');

  for I:= VarArrayLowBound(V, 2) to VarArrayHighBound(V, 2) do
    ComboBox1.Items.Add(V[0, I]));
end;

配列に複数の列が必要な場合は、バリアント配列を 3 番目のパラメーターとして使用する必要があります。

V := ADOQuery1.Recordset.GetRows(adGetRowsRest, EmptyParam, 
       VarArrayOf(['ColumnName1', 'ColumnName2']);
于 2011-11-05T10:28:47.757 に答える
3

Delphi で実装する必要がある、他の人々による優れたパフォーマンスの提案がいくつかあります。それらを考慮する必要があります。私はADOに焦点を当てます。

バックエンド データベース サーバーが何であるかを指定していないので、具体的には言えませんが、ADO について知っておくべきことがいくつかあります。

ADO レコードセット

ADO には、RecordSet オブジェクトがあります。この場合、その RecordSet オブジェクトは基本的に ResultSet です。RecordSet を繰り返し処理することの興味深い点は、それがまだプロバイダーと結合されていることです。

カーソルの種類

カーソル タイプが Dynamic または Delphi のデフォルト キーセットの場合、RecordSet がプロバイダーから新しい行を要求するたびに、プロバイダーはレコードを返す前に変更があったかどうかを確認します。

したがって、結果セットを読み取ってコンボボックスに入力するだけで、変更されている可能性が低い TADOQuery の場合は、静的カーソル タイプを使用して、更新されたレコードのチェックを回避する必要があります。

カーソルが何かわからない場合、Next のような関数を呼び出すと、現在のレコードを表すカーソルが移動します。

すべてのプロバイダがすべてのカーソル タイプをサポートしているわけではありません。

キャッシュサイズ

Delphi と ADO の RecordSet のデフォルトのキャッシュ サイズは 1 です。これは 1 レコードです。これは、カーソル タイプと組み合わせて機能します。cachesize は、一度にフェッチして保存するレコードの数を RecordSet に指示します。

Next のようなコマンド (実際には ADO では MoveNext) をキャッシュ サイズ 1 で発行すると、RecordSet はメモリ内に現在のレコードのみを保持するため、次のレコードをフェッチするときに、プロバイダーから再度要求する必要があります。カーソルが静的でない場合、プロバイダは次のレコードを返す前に最新のデータを取得する機会が与えられます。したがって、更新されたデータをプロバイダーが取得できるようにするため、サイズ 1 は Keyset または Dynamic に適しています。

明らかに、値が 1 の場合、カーソルが移動するたびにプロバイダーと RecordSet の間で通信が行われます。カーソルの種類が静的である場合、これは望ましくないオーバーヘッドです。そのため、キャッシュ サイズを増やすと、RecordSet とプロバイダー間のラウンド トリップの回数が減ります。これによりメモリ要件も増加しますが、より高速になるはずです。

また、キーセット カーソルのキャッシュ サイズが 1 より大きい場合、必要なレコードがキャッシュにある場合は、プロバイダーから再度要求されないため、更新が表示されないことにも注意してください。

于 2011-11-05T15:34:28.357 に答える
3

すべてのデータを ClientDataSet にプッシュしてこれを繰り返すこともできますが、データを CDS にコピーすると、現在行っていること (ループと割り当て) が正確に行われると思います。

私がかつて行ったことは、サーバー上で値を連結し、それをまとめてクライアントに送信し、再度分割することでした。これにより、クライアントとサーバー間で必要な通信が減少したため、実際にシステムが高速化されました。

パフォーマンスのボトルネックがどこにあるかに注意する必要があります。値を追加する際に GUI の更新をブロックしない場合は、コンボボックスでもかまいません (特に 20K の値について話している場合は、スクロールするのが大変です)。

編集:通信を変更できない場合は、おそらく非同期にすることができます。スレッドで新しいデータを要求し、GUI の応答性を維持し、データが存在するときにコンボボックスを埋めます。これは、空のコンボボックスが 5 秒間ユーザーに表示されることを意味しますが、少なくともその間に別のことを行うことができます。ただし、所要時間は変わりません。

于 2011-11-04T23:03:00.603 に答える
3

ループを避けることはできません。「非常に長い時間」は相対的なものですが、20000 件のレコードを取得するのに時間がかかりすぎる場合は、何か問題があります。

  • クエリを確認してください。おそらくSQLを改善できます(インデックスがありませんか?)
  • コンボボックスに項目を追加するループのコードを表示します。もしかしたら最適化できるかもしれません。FieldByName(ループで繰り返し呼び出しますか?バリアントを使用してフィールド値を取得しますか?)
  • ComboBox.Items.BeginUpdate;ループの前後に必ず呼び出してくださいComboBox.Items.EndUpdate
  • プロファイラーを使用してボトルネックを見つけます。
于 2011-11-04T23:13:37.507 に答える
3

クエリは、一部のデータ対応コントロールまたは TDataSource にも関連付けられていますか? その場合は、新しいレコードに移動するたびにビジュアル コントロールが更新されないように、DisableControls および EnableControls ブロック内でループを実行します。

項目のリストはかなり静的ですか? その場合は、アプリケーションの起動時に、おそらく別のスレッド内でコンボボックスの非ビジュアル インスタンスを作成し、フォームの作成時に非ビジュアル コンボボックスをビジュアル コンボボックスに割り当てることを検討してください。

于 2011-11-05T04:15:41.223 に答える
1

データセットの線形プロセスのパフォーマンスを向上させるために、DisableControls と EnableControls を使用してみてください。

   var
  SL: TStringList;
  Fld: TField;
begin
  SL := TStringList.Create;
  AdoQuery1.DisableControls;
  Fld := AdoQuery1.FieldByName('ListFieldName'); 
  try
    SL.Sorted := False; // Sort in the query itself first
    SL.Capacity := 25000; // Some amount estimate + fudge factor

    SL.BeginUpdate;  
    try
      while not AdoQuery1.Eof do
      begin
        SL.Append(Fld.AsString);
        AdoQuery1.Next;
      end;
    finally
      SL.EndUpdate;
    end;

    YourComboBox.Items.AddStrings(SL);

  finally
    SL.Free;
    AdoQuery1.EnableControls;
  end;
end;
于 2012-05-03T18:25:38.383 に答える
0

これが役立つかどうかはわかりませんが、 に直接追加しないことをお勧めしComboBoxます。TStringList代わりにローカルにロードし、それをできるだけ速くしTComboBox.Items.AddStringsてから、一度にすべてを追加するために使用します。

var
  SL: TStringList;
  Fld: TField;
begin
  SL := TStringList.Create;
  Fld := AdoQuery1.FieldByName('ListFieldName'); 
  try
    SL.Sorted := False; // Sort in the query itself first
    SL.Capacity := 25000; // Some amount estimate + fudge factor
    SL.BeginUpdate;  
    try
      while not AdoQuery1.Eof do
      begin
        SL.Append(Fld.AsString);
        AdoQuery1.Next;
      end;
    finally
      SL.EndUpdate;
    end;
    YourComboBox.Items.BeginUpdate;
    try
      YourComboBox.Items.AddStrings(SL);
    finally
      YourComboBox.Items.EndUpdate;
    end;
  finally
    SL.Free;
  end;
end;
于 2011-11-05T01:12:37.560 に答える