我们正在尝试从TADOQuery中找到一些性能修复程序。目前,我们使用'while not Q.eof do begin ... Q.next方法循环记录。对于每个记录,我们读取每个记录的ID和值,并将每个记录添加到组合框列表中。
有没有办法将指定字段的所有值一次性转换为列表?而不是循环数据集?如果我可以做类似的事情,那将会非常方便。
TStrings(MyList).Assign(Q.ValuesOfField['Val']);
我知道这不是一个真正的命令,但那是我正在寻找的概念。寻找快速响应和解决方案(一如既往,但这是为了解决一个非常紧急的性能问题)。
答案 0 :(得分:13)
查看您的评论,以下是一些建议:
在这种情况下,有一些事情可能成为瓶颈。第一个是重复查看字段。如果您在循环中调用FieldByName
或FindField
,则会浪费CPU时间重新计算一个不会改变的值。为您正在读取的每个字段调用一次FieldByName,并将它们分配给局部变量。
从字段中检索值时,请调用AsString
或AsInteger
或其他返回您正在查找的数据类型的方法。如果您正在阅读TField.Value
媒体资源,那么您在variant
次转化时就会浪费时间。
如果您要将一堆项添加到Delphi组合框中,您可能正在以Items
属性的形式处理字符串列表。设置列表的Capacity
属性,并确保在开始更新前致电BeginUpdate
,并在结束时致电EndUpdate
。这可以实现一些内部优化,从而加快大量数据的加载速度。
根据您使用的组合框,处理其内部列表中的大量项目可能会遇到一些问题。看看它是否具有“虚拟”模式,而不是你预先加载所有内容,你只需告诉它需要多少项,当它被下拉时,它会调用每个应该显示的项目的事件处理程序在屏幕上,你给它正确的文字显示。这确实可以加速某些UI控件。
此外,您应该确保数据库查询本身很快,但SQL优化超出了这个问题的范围。
最后,Mikael Eriksson的评论绝对值得关注!
答案 1 :(得分:8)
您可以使用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;
如果你想在数组中有多个列,你应该使用变量数组作为第三个参数。
V := ADOQuery1.Recordset.GetRows(adGetRowsRest, EmptyParam,
VarArrayOf(['ColumnName1', 'ColumnName2']);
答案 2 :(得分:3)
您可以尝试将所有数据推送到ClientDataSet并对其进行迭代,但我认为将数据复制到CDS正是您正在进行的操作 - 循环和分配。
我曾经做过的是在服务器上连接值,将其一个批量传输到客户端并再次拆分。这实际上使系统更快,因为它减少了客户端和服务器之间的通信。
您必须仔细考虑性能瓶颈所在的位置。如果你在添加值时不阻止GUI更新,那么它也可以是组合框(特别是当我们谈论20K值时 - 滚动很多)。
编辑:当您无法更改通信时,您可能会使其异步。在线程中请求新数据,保持GUI响应,在数据存在时填充组合框。这意味着用户看到空组合框5秒钟,但至少他可以在此期间做其他事情。但是,不会改变所需的时间。
答案 3 :(得分:3)
你无法避免循环。 “很长时间”是相对的,但如果检索20000条记录花费的时间太长则会出现问题。
FieldByName
?使用变量来检索字段值?)ComboBox.Items.BeginUpdate;
,然后在ComboBox.Items.EndUpdate
之后调用。答案 4 :(得分:3)
您的查询是否也与某些数据感知控件或TDataSource相关联?如果是这样,请在DisableControls和EnableControls块中循环,这样每次移动到新记录时,可视控件都不会更新。
项目列表是否相当静态?如果是这样,请考虑在应用程序启动时创建组合框的非可视实例,可能在单独的线程内,然后在创建表单时将非可视组合框分配给可视组合框。
答案 5 :(得分:3)
其他人提出了一些很好的性能建议,你应该在Delphi中实现。你应该考虑他们。我将专注于ADO。
您还没有指定后端数据库服务器是什么,所以我不能太具体,但是您应该了解一些关于ADO的事情。
ADO RecordSet
在ADO中,有一个RecordSet对象。在这种情况下,RecordSet对象基本上就是您的ResultSet。迭代RecordSet的有趣之处在于它仍然与提供者结合。
光标类型
如果您的光标类型是Dynamic或Delphi的默认Keyset,那么每次RecordSet从提供者请求新行时,提供者都会在返回记录之前检查是否有任何更改。
因此,对于TADOQuery,你所做的只是读取结果集以填充组合框,并且它不太可能已经改变,你应该使用静态游标类型来避免检查更新的记录。
如果你不知道光标是什么,当你调用像Next这样的函数时,你正在移动代表当前记录的光标。
并非每个提供程序都支持所有游标类型。
<强> CacheSize的强>
Delphi和ADO的RecordSet默认缓存大小为1.这是1条记录。这与光标类型结合使用。 cachesize告诉RecordSet一次获取和存储多少条记录。
当您发出一个高速缓存大小为1的Next(在ADO中真的是MoveNext)之类的命令时,RecordSet只在内存中有当前记录,因此当它获取下一条记录时,它必须再次从提供程序请求它。如果游标不是静态的,则可以使提供程序在返回下一条记录之前获取最新数据。因此,大小为1对于Keyset或Dynamic是有意义的,因为您希望提供程序能够获取更新的数据。
显然,值为1时,每次移动光标时,提供者和RecordSet之间都会进行通信。好吧,如果游标类型是静态的,那就是我们不想要的开销。因此,增加缓存大小将减少RecordSet和提供程序之间的往返次数。这也会增加你的内存需求,但它应该更快。
另请注意,如果Keyset游标的缓存大小超过1,如果所需的记录位于缓存中,则它不会再次从提供程序请求它,这意味着您将看不到更新。
答案 6 :(得分: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;
答案 7 :(得分: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;