Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/oracle/10.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
Performance Delphi中的BDE与ADO_Performance_Oracle_Delphi_Ado_Bde - Fatal编程技术网

Performance Delphi中的BDE与ADO

Performance Delphi中的BDE与ADO,performance,oracle,delphi,ado,bde,Performance,Oracle,Delphi,Ado,Bde,请注意下面的编辑,了解更多信息和可能的解决方案 我们最近修改了一个大型Delphi应用程序,使用ADO连接和查询,而不是BDE连接和查询。自那次改变以来,业绩变得糟糕 我已经分析了应用程序,瓶颈似乎是在实际调用TADOQuery.Open时出现的。换句话说,从代码的角度来看,除了重新构造应用程序以减少对数据库的实际使用之外,我无法做多少改进 有人对如何提高ADO连接的Delphi应用程序的性能有什么建议吗?我试过这两种方法,几乎没有效果 为了了解性能差异,我对相同的大型操作进行了基准测试: B

请注意下面的编辑,了解更多信息和可能的解决方案

我们最近修改了一个大型Delphi应用程序,使用ADO连接和查询,而不是BDE连接和查询。自那次改变以来,业绩变得糟糕

我已经分析了应用程序,瓶颈似乎是在实际调用
TADOQuery.Open
时出现的。换句话说,从代码的角度来看,除了重新构造应用程序以减少对数据库的实际使用之外,我无法做多少改进

有人对如何提高ADO连接的Delphi应用程序的性能有什么建议吗?我试过这两种方法,几乎没有效果

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

  • BDE下:11秒

  • 在ADO下:73秒

  • 在该文章引用的更改之后,在ADO下:72秒

我们在客户机-服务器环境中使用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=*******';
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
回答zendar提出的问题:

我在WindowsVista和XP上使用Delphi2007

后端是一个Oracle 10g数据库

如连接字符串所示,我们正在使用OraOLEDB驱动程序

我的基准机器上的MDAC版本是6.0

编辑:

var
   qry: TADOQuery;
begin
   qry := TADOQuery.Create(nil);
   try
      qry.SQL.Add(CommandText);
      qry.Open;
      while not qry.EOF do
      begin
         ...
         qry.Next;
      end;
在BDE下,我们有很多代码如下所示:

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=*******';
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

更好的是,您可以从ADODB.pas中复制
TADOQuery
,用一个新名称重命名它,然后删除
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;

我不知道Delphi2007,但我对Delphi7和Oracle8做了同样的事情

以下是我所做的事情:

  • 根据查询设置TAdoDataSet.CursorLocation
    • ClusClient如果查询为GUI获取记录,并且查询相对“简单”-没有分组或求和
    • clUseServer如果查询具有某种聚合(求和、分组、计数)
  • 根据查询设置TAdoDataSet.CursorType
    • ctForwardOnly适用于不需要通过数据集向后滚动的报告-仅适用于clUseServer
    • ctStatic用于GUI。这是唯一适用于ClusClient的模式
  • 根据查询设置TAdoDataSet.LockType
    • ltReadOnly对于每个不用于编辑的数据集(网格、报告)
    • ltOptimistic当记录在更改后立即发布到数据库时(例如,用户编辑表单上的数据)
    • 当您更改大量记录时。这适用于获取大量记录,然后对其进行一些处理,然后批量向数据库发送更新的情况。这与ClusClient和ctStatic结合使用效果最佳
  • 根据我的经验,Microsoft OLEDB provider for Oracle比Oracle OLEDB provider工作得更好。你应该测试一下
    编辑:检查Fabrico对可能出现的blob问题的评论
  • TAdoDataSet替换TAdoQUery。TAdoQuery是为了将应用程序从BDE转换为ADO而创建的,但Borland/Codegear建议使用TAdoDataSet
  • 重新检查Oracle连接字符串以确保没有网络延迟。连接到Oracle需要多长时间?TnsPing有多长时间

    • 要获得最佳性能,请查看我们的

      如果您在不使用DB组件的情况下处理大量TQuery,我们有一个专用的伪类来使用直接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时都存在缺陷:Microsoft版本不处理BLOB,Oracle版本将


      在real database上,我发现我们的direct OCI类比OleDB提供程序快2到5倍,而无需安装此提供程序。您甚至可以使用Oracle提供的,它允许您运行应用程序,而无需安装标准(大型)Oracle客户端或拥有Oracle_主页。只需将dll文件交付到与应用程序相同的目录中,它就可以工作。

      多年前,我发现ADOExpress存在性能问题:

      • (6/7/2005)
      • (12/30/2007)
      注意:在ADO成为Delphi的标准部分之前,Borland将其作为名为ADOExpress的插件出售。它只是围绕微软的ActiveX数据对象(ADO)COM对象的对象包装器

      我测试了三个场景

      • 直接使用ADO(即直接使用Microsoft的COM对象)
      • 使用ADOExpress(Borland围绕ADO的对象包装器)
      • 在调用
        Open
      我发现

      • 使用
        Query.DisableControls
        使每次调用
        。Next
        快50倍
      • 使用
        Query.Recordset.Fields.Items['columnName'].Value
        而不是
        Query.FieldByName('columnName')
        使每个值的查找速度加快2.7倍
      • 使用
        TADODataSet
        (诗句
        TADOQuery
        )没有区别

                                        Loop Results        Get Values 
        ADOExpress:                         28.0s              46.6s 
        ADOExpress w/DisableControls:        0.5s              17.0s 
        ADO (direct use of interfaces):      0.2s               4.7s 
        
      注意:这些值用于循环20
      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;