Delphi 将数百万条记录加载到stringlist可能非常缓慢
如何快速地将数以百万计的记录从TaToTable加载到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
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'