首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Delphi中的BDE与ADO

Delphi中的BDE与ADO
EN

Stack Overflow用户
提问于 2008-12-15 17:55:47
回答 3查看 14.7K关注 0票数 13

请注意下面的编辑以获得更多信息和可能的解决方案

我们最近修改了一个大型Delphi应用程序,以使用ADO连接和查询,而不是BDE连接和查询。自从这一变化之后,表现就变得很糟糕。

我已经分析了应用程序,瓶颈似乎是在实际调用TADOQuery.Open时遇到的。换句话说,除了对应用程序进行重构以减少对数据库的实际使用之外,从代码的角度来看,我没有什么可以改进的。

有没有人对如何提高与ADO连接的Delphi应用程序的性能有建议?我已经尝试过这两个在此提出的建议,几乎没有任何影响。

为了了解性能差异,我对相同的大型操作进行了基准测试:

  • 低于BDE: 11秒
  • 在ADO下: 73秒
  • 在该条引用的更改后的ADO下: 72秒

我们在客户端服务器环境中使用Oracle后端。本地机器各自维护到数据库的单独连接。

对于记录,连接字符串如下所示:

代码语言:javascript
复制
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下,我们有很多代码如下所示:

代码语言:javascript
复制
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事件。所以用这个代替上面的东西要快得多:

代码语言:javascript
复制
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版本,而不是本机版本。

代码语言:javascript
复制
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;
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2008-12-15 18:23:17

我不知道Delphi 2007,但我对Delphi 7和Oracle 8也做了同样的事情。

以下是我所做的事情:

  • 根据查询设置TAdoDataSet.CursorLocation
    • clUseClient如果查询为GUI获取记录,并且查询相对“简单”--没有分组或和
    • clUseServer如果查询有某种聚合(和、分组、计数)

  • 根据查询设置TAdoDataSet.CursorType
    • ctForwardOnly用于不需要在dataset中回滚的报表--仅适用于clUseServer
    • ctStatic for GUI。这是仅适用于clUseClient的模式。

  • 根据查询设置TAdoDataSet.LockType
    • 不用于编辑(网格、报表)的每个数据集的ltReadOnly
    • ltOptimistic当记录在更改后立即发布到数据库时(例如,用户在表单上编辑数据)
    • 当您更改大量记录时,ltBatchOptimistic。这是针对这样的情况:获取记录的数量,然后对它们进行一些处理,然后批量地向数据库发送更新。这与clUseClient和ctStatic最好地结合在一起。

  • 根据我的经验,比工作得更好。你应该测试一下。 编辑:检查Fabricio关于可能的blob问题的评论。
  • TAdoDataSet.替换TAdoQUeryTAdoQuery是为将应用程序从BDE转换为ADO而创建的,但Borland/则使用TAdoDataSet。
  • 重新检查Oracle连接字符串,以确保没有网络延迟。连接到Oracle需要多长时间?TnsPing多长时间?
票数 14
EN

Stack Overflow用户

发布于 2011-07-16 14:52:29

多年前,我发现了ADOExpress的性能问题:

  • ADO对ADOExpress时间试验。对ADOExpress不好 (2005年7月6日)
  • ADO对ADO快速时间审判(redux) (12/30/2007)

备注:在ADO成为Delphi的标准部分之前,就把它作为一个名为ADOExpress的副词出售。它只是微软的ActiveX数据对象(ADO)对象的对象包装器。

我已经测试了三种方案

  • 直接使用ADO (即直接使用Microsoft的COM对象)
  • 使用ADOExpress (Borland的对象包装器环绕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列的值。

基线坏代码:

代码语言:javascript
复制
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%

代码语言:javascript
复制
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%

代码语言:javascript
复制
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;

由于这是一个非常常见的问题,我们创建了一个帮助方法来解决这个问题:

代码语言:javascript
复制
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;
票数 5
EN

Stack Overflow用户

发布于 2011-07-11 06:15:02

为了获得最好的性能,应该看看我们的开放源码直接访问Oracle

如果您正在处理大量的TQuery,而不使用DB组件,那么我们有一个专用的伪类来使用直接的OCI连接,如下所示:

代码语言:javascript
复制
 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;

我还通过一个后期绑定的变体添加了一些独特的访问权限,以这样的方式编写直接代码:

代码语言:javascript
复制
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文件,它就能工作。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/369187

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档