5

如何快速将 tadotable 中的数百万条记录加载到字符串列表中?

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 的答案,我建议使用以下代码:

确保您的 Query 没有连接到任何可视化的其他组件,并且没有设置任何在 rowchanges 上触发的事件,因为这将导致它更新活动记录中的每个更改,从而减慢速度。
您可以使用 禁用可视控件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 语句中执行此操作。数据库可以使用字符串列表无法使用的索引和其他优化。
如果您想将该数据保存到 CSV 文件中,请考虑为此使用内置 DB 函数。
例如 MySQL 有:

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

这将为您创建一个 CSV 文件。
许多数据库具有类似的功能。

于 2011-12-08T10:33:06.217 回答
0

严重地?字符串列表中有数百万条记录?

好的,假设您确实需要采用这种方法...

已经发布了一些很好的建议。

如果您想尝试不同的方法,您可以考虑连接各个记录服务器端(通过存储过程),然后将连接的数据作为 blob(或者可能是 nvarchar(max))返回,这基本上是连接字符串的列表由回车分隔(假设这是您需要的合理分隔符)。

然后,您可以简单地将返回值分配给 TStringList 的 Text 属性。

即使你不能一次敲定所有的弦,你也可以一次一组地做 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 回答