Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 将数百万条记录加载到stringlist可能非常缓慢_Delphi_Tstringlist_Tadotable - Fatal编程技术网

Delphi 将数百万条记录加载到stringlist可能非常缓慢

Delphi 将数百万条记录加载到stringlist可能非常缓慢,delphi,tstringlist,tadotable,Delphi,Tstringlist,Tadotable,如何快速地将数以百万计的记录从TaToTable加载到stringlist中 procedure TForm1.SlowLoadingIntoStringList(StringList: TStringList); begin StringList.Clear; with SourceTable do begin Open; DisableControls; try while not EOF do begin StringLi

如何快速地将数以百万计的记录从TaToTable加载到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;

不幸的是,你不能很快做到这一点。这是一种固有的缓慢操作,需要大量CPU时间和内存带宽才能实现。你可以投入更多的硬件,但我怀疑你应该重新考虑你的任务。

在你的循环中,你得到了字段。 从循环中搜索字段

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;
程序TForm1.slowLoadingToStringList(StringList:TStringList);
变量
奥菲尔德:特菲尔德;
开始
StringList.Clear;
使用SourceTable do
开始
打开
禁用控制;
尝试
oField:=FieldByName('OriginalData');
如果没有,那么
开始
而不是EOF做什么
开始
添加(oField.AsString);
下一个
结束;
结束;
最后
使能控制;
接近;
结束;
结束;
结束;
是否已排序

  // 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;

你可以考虑“数百万的记录”: 1/将您的查询从

SELECT * FROM MYTABLE;

您将使用更少的内存,效率更高

2/根据您的需要,查看TStringList以外的其他组件

3/查看所有以前的好建议,主要是:

  • 不要使用FieldByName
  • 直接链接到OleDB提供程序

    • 真的吗?stringlist中有数百万条记录

      好吧,假设你真的需要采取这种方法

      已经发布了一些好的建议

      如果您想尝试一种不同的方法,可以考虑将单个记录服务器端连接起来(通过存储过程),然后将级联数据返回为BLUB(或者可能是NVARCHAR(MAX)),这基本上是由回车符划定的级联字符串的列表。(假设这是符合您需要的合理分隔符)

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

      即使你不能在一次点击中完成所有的弦乐,你也可以一次1000人一组完成


      这将节省您在每个记录客户端循环的大量时间。

      扩展@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可以使用索引和其他优化,而StrugList不能使用这些优化。 如果您想将这些数据保存到CSV文件中,请考虑使用内置的DB函数。 e、 g.MySQL有:

      SELECT X FROM table1 INTO OUTFILE 'c:/filename_of_csv_file.txt'
      
      这将为您创建一个CSV文件。

      许多数据库都有simular功能。

      这里的瓶颈可能是数据库和/或ado驱动程序,而不是stringlist,不过您可以尝试分析以确定。如果确认,您将需要更快的数据库或更好的驱动程序来加快速度。您可以试着设置stringlist.Capacity:=SourceTable.RecordCount;这将节省大量资源底层阵列的重新分配这里比较慢的是
      FieldByName('OriginalData')
      。并尝试使用到OleDB提供程序的直接链接来绕过ADO层。例如,请参阅我们的.Scrub我之前的评论-我只是尝试了一个非常简单的1000万条记录循环,它只是稍微(大约15%)更快。我还会添加
      BeginUpdate
      EndUpdate
      ,以防有人通过此方法作为输入,例如
      TListBox.Items
      :)不,说真的,Kamyar,加载这么多行的原因是什么?您正在将这些行保存为文件吗?或者进行一些动态搜索?如果是动态搜索,那么您应该让DB服务器为您执行此操作。如果输入是TStringList参数,则无法传递TStrings。+1,让服务器端完成繁重工作的好处。无论如何,我会将输出字符串列表声明为参数,而不是函数结果;)
      SELECT X FROM table1 INTO OUTFILE 'c:/filename_of_csv_file.txt'