5

何百万ものレコードを tadotable から stringlist に非常に高速にロードするにはどうすればよいですか?

procedure TForm1.SlowLoadingIntoStringList(StringList: TStringList);
begin
  StringList.Clear;
  with SourceTable do
  begin
    Open;
    DisableControls;
    try
      while not EOF do
    begin
      StringList.Add(FieldByName('OriginalData').AsString);
      Next;
    end;
   finally
   EnableControls;
   Close;
  end;
end;
4

6 に答える 6

10

ループでフィールドを取得します。ループ外でフィールドを検索する

procedure TForm1.SlowLoadingIntoStringList(StringList: TStringList); 
var
  oField: TField;
begin
  StringList.Clear;   
  with SourceTable do   
  begin     
    Open;     
    DisableControls;     
    try       
      oField:= FieldByName('OriginalData');
      if oField<>Nil then
      begin
        while not EOF do
        begin       
          StringList.Add(oField.AsString);       
          Next;     
        end;   
      end; 
    finally    
      EnableControls;    
      Close;   
    end; 
  end;  
end;
于 2011-12-07T11:55:38.317 に答える
4

残念ながら、これをすぐに行うことはできません。これは、達成するために大量の CPU 時間とメモリ帯域幅を必要とする、本質的に低速な操作です。より多くのハードウェアを投入することもできますが、代わりにタスクを再考する必要があると思います.

于 2011-12-07T11:20:45.337 に答える
1

「数百万のレコード」を使用すると、次のように考えることができます:1 /クエリを次から変更します

SELECT * FROM MYTABLE;

SELECT OriginalData FROM MYTABLE;

メモリの使用量が減り、効率が向上します。

2/ 必要に応じて、TStringList 以外のコンポーネントを探します。

3/ 主に :

  • FieldByName を使用しないでください
  • OleDB プロバイダーへの直接リンク
于 2011-12-07T18:08:05.983 に答える
0

@ Ravaut123の回答を拡張して、次のコードを提案します。

クエリが視覚的な他のコンポーネントに接続されていないこと、および行の変更時に発生するイベントが設定されていないことを確認してください。これにより、アクティブレコードのすべての変更で更新が行われ、処理速度が低下します。
を使用してビジュアルコントロールを無効にすることはできますがdisablecontrols、イベントや非ビジュアルコントロールを無効にすることはできません。

...
SQLatable:= 'SELECT SingleField FROM atable ORDER BY indexedfield ASC';
AQuery:= TAdoQuery.Create(Form1);
AQuery.Connection:= ....
AQuery.SQL.Text:= SQLatable;  

クエリを使用すると、必要な順序で1つのフィールドのみを選択するようになり、ネットワークトラフィックが減少します。テーブルはすべてのフィールドをフェッチするため、オーバーヘッドが大幅に増加します。

function TForm1.LoadingAllIntoStringList(AQuery: TAdoQuery): TStringList;  
var 
  Field1: TField; 
begin 
  Result:= nil;
  try
    if not(AQuery.Active) then begin
      AQuery.Open;
    end else begin
      AQuery.First;
    end;
    AQuery.DisableControls;
    AQuery.Filtered:= false;                    //Filter in the SQL `where` clause
    AQuery.FetchAll;                            //Preload all data into memory
    Result:= TStringlist.Create;
  except
    {ignore error, will return nil}
  end;
  try
    Result.Sorted:= false;                      //Make sure you don't enable sorting
    Result.Capacity:= AQuery.RecordCount;       //Preallocate the needed space     
    Field1:= AQuery.FieldByName('SingleField'); //Never use `fieldbyname` in a loop!
    while not AQuery.EOF do begin
      Result.Add(Field1.AsString);
      AQuery.Next;
    end; {while} 
    AQuery.EnableControls;
  except
    FreeAndNil(Result);
  end;   

何らかの処理を行うためにデータを文字列リストにロードする場合は、代わりにSQLステートメントでそれを行うことを検討してください。DBは、stringlistでは使用できないインデックスやその他の最適化を使用できます。
そのデータをCSVファイルに保存する場合は、組み込みのDB関数を使用することを検討してください。
たとえば、MySQLには次のものがあります。

SELECT X FROM table1 INTO OUTFILE 'c:/filename_of_csv_file.txt'

これにより、CSVファイルが作成されます。
多くのDBには同様の機能があります。

于 2011-12-08T10:33:06.217 に答える
0

真剣に?文字列リストに何百万ものレコード?

わかりました、あなたが本当にこのアプローチをとる必要があるとしましょう...

いくつかの良い提案がすでに投稿されています。

別のアプローチを試してみたい場合は、個々のレコード サーバー側を (ストアド プロシージャを介して) 連結し、連結されたデータを blob (または場合によっては nvarchar(max)) として返すことを検討できます。これは基本的に連結された文字列のリストです。改行などで区切られます (これがニーズに適した区切り文字であると仮定します)。

次に、戻り値を TStringList の Text プロパティに割り当てるだけです。

1回のヒットですべてのストリングを実行できなくても、一度にたとえば1000のグループで実行できます.

これにより、各レコード クライアント側でループする時間を大幅に節約できます。

于 2011-12-08T08:16:45.150 に答える
0

ソートされていますか?

  // Turn off the sort for now
  StringList.Sorted := False;
  // Preallocate the space
  StringList.Capacity := recordCount;
  // Now add the data with Append()
  ...
  // Now turn the sort back on
  StringList.Sorted := True;
于 2011-12-07T16:01:16.710 に答える