请注意下面的编辑以获得更多信息和可能的解决方案
我们最近修改了一个大型Delphi应用程序,以使用ADO连接和查询,而不是BDE连接和查询。自从这一变化之后,表现就变得很糟糕。
我已经分析了应用程序,瓶颈似乎是在实际调用TADOQuery.Open时遇到的。换句话说,除了对应用程序进行重构以减少对数据库的实际使用之外,从代码的角度来看,我没有什么可以改进的。
有没有人对如何提高与ADO连接的Delphi应用程序的性能有建议?我已经尝试过这两个在此提出的建议,几乎没有任何影响。
为了了解性能差异,我对相同的大型操作进行了基准测试:
我们在客户端服务器环境中使用Oracle后端。本地机器各自维护到数据库的单独连接。
对于记录,连接字符串如下所示:
const
c_ADOConnString = 'Provider=OraOLEDB.Oracle.1;Persist Security Info=True;' +
'Extended Properties="plsqlrset=1";' +
'Data Source=DATABASE.DOMAIN.COM;OPTION=35;' +
'User ID=******;Password=*******';要回答zendar提出的问题:
我在Windows和XP上使用Delphi 2007。
后端是一个Oracle 10g数据库。
如连接字符串所示,我们使用的是OraOLEDB驱动程序。
我的基准测试机器上的MDAC版本是6.0。
编辑:
在BDE下,我们有很多代码如下所示:
procedure MyBDEProc;
var
qry: TQuery;
begin
//fast under BDE, but slow under ADO!!
qry := TQuery.Create(Self);
try
with qry do begin
Database := g_Database;
Sql.Clear;
Sql.Add('SELECT');
Sql.Add(' FIELD1');
Sql.Add(' ,FIELD2');
Sql.Add(' ,FIELD3');
Sql.Add('FROM');
Sql.Add(' TABLE1');
Sql.Add('WHERE SOME_FIELD = SOME_CONDITION');
Open;
//do something
Close;
end; //with
finally
FreeAndNil(qry);
end; //try-finally
end; //proc但是我们发现在ADO下对Sql.Add的调用实际上非常昂贵,因为每次更改CommandText时都会触发QueryChanged事件。所以用这个代替上面的东西要快得多:
procedure MyADOProc;
var
qry: TADOQuery;
begin
//fast(er) under ADO
qry := TADOQuery.Create(Self);
try
with qry do begin
Connection := g_Connection;
Sql.Text := ' SELECT ';
+ ' FIELD1 '
+ ' ,FIELD2 '
+ ' ,FIELD3 '
+ ' FROM '
+ ' TABLE1 '
+ ' WHERE SOME_FIELD = SOME_CONDITION ';
Open;
//do something
Close;
end; //with
finally
FreeAndNil(qry);
end; //try-finally
end; //proc更好的是,您可以从TADOQuery中复制ADODB.pas,将其重命名为一个新名称,并删除QueryChanged事件,据我所知,该事件根本没有做任何有用的事情。然后使用新的、修改过的TADOQuery版本,而不是本机版本。
type
TADOQueryTurbo = class(TCustomADODataSet)
private
//
protected
procedure QueryChanged(Sender: TObject);
public
FSQL: TWideStrings;
FRowsAffected: Integer;
function GetSQL: TWideStrings;
procedure SetSQL(const Value: TWideStrings);
procedure Open;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function ExecSQL: Integer; {for TQuery compatibility}
property RowsAffected: Integer read FRowsAffected;
published
property CommandTimeout;
property DataSource;
property EnableBCD;
property ParamCheck;
property Parameters;
property Prepared;
property SQL: TWideStrings read FSQL write SetSQL;
end;
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
constructor TADOQueryTurbo.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FSQL := TWideStringList.Create;
TWideStringList(FSQL).OnChange := QueryChanged;
Command.CommandText := 'SQL'; { Do not localize }
end;
destructor TADOQueryTurbo.Destroy;
begin
inherited;
inherited Destroy;
FreeAndNil(FSQL);
end;
function TADOQueryTurbo.ExecSQL: Integer;
begin
CommandText := FSQL.Text;
inherited;
end;
function TADOQueryTurbo.GetSQL: TWideStrings;
begin
Result := FSQL;
end;
procedure TADOQueryTurbo.Open;
begin
CommandText := FSQL.Text;
inherited Open;
end;
procedure TADOQueryTurbo.QueryChanged(Sender: TObject);
begin
// if not (csLoading in ComponentState) then
// Close;
// CommandText := FSQL.Text;
end;
procedure TADOQueryTurbo.SetSQL(const Value: TWideStrings);
begin
FSQL.Assign(Value);
CommandText := FSQL.Text;
end;发布于 2008-12-15 18:23:17
我不知道Delphi 2007,但我对Delphi 7和Oracle 8也做了同样的事情。
以下是我所做的事情:
发布于 2011-07-16 14:52:29
多年前,我发现了ADOExpress的性能问题:
备注:在ADO成为Delphi的标准部分之前,就把它作为一个名为ADOExpress的副词出售。它只是微软的ActiveX数据对象(ADO)对象的对象包装器。
我已经测试了三种方案
.DisableControls之前在TADOQuery上指定Open我发现
Query.DisableControls使每次调用.Next的速度快50倍Query.Recordset.Fields.Items['columnName'].Value而不是Query.FieldByName('columnName')使每个值查找速度快2.7倍TADODataSet (诗句TADOQuery)没有什么区别。
循环结果得到值ADOExpress: 28.046s ADOExpress w/DisableControls: 0.5s17.06ADO(直接使用接口):0.2s4.7s注意事项:这些值用于循环20,881行,并查找21列的值。
基线坏代码:
var
qry: TADOQuery;
begin
qry := TADOQuery.Create(nil);
try
qry.SQL.Add(CommandText);
qry.Open;
while not qry.EOF do
begin
...
qry.Next;
end;使用DisableControls使循环速度提高了5000%
var
qry: TADOQuery;
begin
qry := TADOQuery.Create(nil);
try
qry.DisableControls;
qry.SQL.Add(CommandText);
qry.Open;
while not qry.EOF do
begin
...
qry.Next;
end;使用字段集合使值查找速度提高了270%
var
qry: TADOQuery;
begin
qry := TADOQuery.Create(nil);
try
qry.DisableControls;
qry.SQL.Add(CommandText);
qry.Open;
while not qry.EOF do
begin
value1 := VarAsString(qry.Recordset.Fields['FieldOne'].Value);
value2 := VarAsInt(qry.Recordset.Fields['FieldTwo'].Value);
value3 := VarAsInt64(qry.Recordset.Fields['FieldTwo'].Value);
value4 := VarAsFloat(qry.Recordset.Fields['FieldThree'].Value);
value5 := VarAsWideString(qry.Recordset.Fields['FieldFour'].Value);
...
value56 := VarAsMoney(qry.Recordset.Fields['FieldFive'].Value);
qry.Next;
end;由于这是一个非常常见的问题,我们创建了一个帮助方法来解决这个问题:
class function TADOHelper.Execute(const Connection: TADOConnection;
const CommandText: WideString): TADOQuery;
var
rs: _Recordset;
query: TADOQuery;
nRecords: OleVariant;
begin
Query := TADOQuery.Create(nil);
Query.DisableControls; //speeds up Query.Next by a magnitude
Query.Connection := Connection;
Query.SQL.Text := CommandText;
try
Query.Open();
except
on E:Exception do
begin
Query.Free;
raise;
end;
end;
Result := Query;
end;发布于 2011-07-11 06:15:02
为了获得最好的性能,应该看看我们的开放源码直接访问Oracle。
如果您正在处理大量的TQuery,而不使用DB组件,那么我们有一个专用的伪类来使用直接的OCI连接,如下所示:
Q := TQuery.Create(aSQLDBConnection);
try
Q.SQL.Clear; // optional
Q.SQL.Add('select * from DOMAIN.TABLE');
Q.SQL.Add(' WHERE ID_DETAIL=:detail;');
Q.ParamByName('DETAIL').AsString := '123420020100000430015';
Q.Open;
Q.First; // optional
while not Q.Eof do begin
assert(Q.FieldByName('id_detail').AsString='123420020100000430015');
Q.Next;
end;
Q.Close; // optional
finally
Q.Free;
end;我还通过一个后期绑定的变体添加了一些独特的访问权限,以这样的方式编写直接代码:
procedure Test(Props: TOleDBConnectionProperties; const aName: RawUTF8);
var I: ISQLDBRows;
Customer: Variant;
begin
I := Props.Execute('select * from Domain.Customers where Name=?',[aName],@Customer);
while I.Step do
writeln(Customer.Name,' ',Customer.FirstName,' ',Customer.Address);
end;
var Props: TOleDBConnectionProperties;
begin
Props := TSQLDBOracleConnectionProperties.Create(
'TnsName','UserName','Password',CODEPAGE_US);
try
Test(Props,'Smith');
finally
Props.Free;
end;
end;请注意,所有的OleDB提供程序在处理BLOB方面都是错误的:微软的版本只是不处理它们,而对1/4行随机返回null的版本将.
在真实的数据库中,我发现我们的直接OCI类比OleDB提供程序快2到5倍,而不需要安装这个提供程序。您甚至可以使用Oracle提供的Oracle即时客户端,它允许您在不安装标准(庞大) Oracle客户端或拥有ORACLE_HOME的情况下运行应用程序。只需在与应用程序相同的目录中传递dll文件,它就能工作。
https://stackoverflow.com/questions/369187
复制相似问题