programing

델파이의 BDE 대 ADO

minimums 2023. 8. 1. 20:24
반응형

델파이의 BDE 대 ADO

자세한 내용과 가능한 해결 방법은 아래의 편집을 참조하십시오.

우리는 최근 BDE 연결과 쿼리 대신 ADO 연결과 쿼리를 사용하도록 대규모 Delphi 애플리케이션을 수정했습니다.그 변화 이후로, 성능은 형편없게 되었습니다.

프로파일을 있는 것 .TADOQuery.Open 실제로 덜 하는 것 에 코드 에서 이를 할 수 일은 않습니다.", 코관에서이를개즉선점하기이스베데이터해를제실도없다니습별록일로수할드이있재것애하로외는는하션성을구는에이케리플용사덜위▁in즉▁standation▁to▁there▁applic▁this▁less▁from,▁improve▁do▁other,다없니,습▁to▁a▁wordspoint▁database▁i별▁restruct▁the▁can코▁code▁isn로'일▁than▁actuallyuring▁the▁other▁mucht이▁use드있수점는할관것외

ADO 연결 델파이 애플리케이션의 성능을 개선하는 방법에 대해 제안하는 사람이 있습니까?저는 여기에 제시된 두 가지 제안을 모두 시도했지만 사실상 아무런 영향을 미치지 않았습니다.

성능 차이를 파악하기 위해 동일한 대규모 작업을 벤치마킹했습니다.

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

젠더가 제기한 질문에 답하기 위해:

Windows Vista 및 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

는 하만우그전가화로 가 온 했습니다.Sql.Add. ADO에서는QueryChanged▁the▁change▁is를 변경할 때마다 실행됩니다.CommandText따라서 위의 내용을 이것으로 대체하는 것이 훨씬 더 빨랐습니다.

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 대신 수정된 새 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;

Delphi 2007에 대해서는 잘 모르지만, 저는 Delphi 7과 Oracle 8에 대해서도 같은 일을 했습니다.

제가 한 일은 다음과 같습니다.

  • TADoDataSet을 설정합니다.쿼리에 따른 커서 위치:
    • 쿼리가 GUI에 대한 레코드를 가져오고 쿼리가 비교적 "단순"한 경우 clUseClient - 그룹화 또는 합계 없음
    • 쿼리에 집계(합, 그룹화, 카운트)가 있는 경우 clUseServer
  • TADoDataSet을 설정합니다.쿼리에 따른 커서Type:
    • ctForward데이터 세트를 스크롤할 필요가 없는 보고서의 경우에만 clUseServer에서만 작동합니다.
    • ctStatic(GUI의 경우). 모드는 clUseClient에서만 작동합니다.
  • TADoDataSet을 설정합니다.쿼리에 따른 잠금 유형:
    • 편집에 사용되지 않는 모든 데이터 세트(그리드, 보고서)에 대한 ltReadOnly
    • lt변경 직후 레코드가 데이터베이스에 게시될 때 최적(예: 양식의 데이터 편집)
    • 많은 수의 레코드를 변경할 때 ltBatch Optimic.이것은 레코드 수를 가져온 다음 레코드에 대해 일부 처리를 수행한 다음 데이터베이스에 업데이트를 일괄 전송하는 상황을 위한 것입니다.이 기능은 clUseClient 및 ctStatic과 함께 사용하는 것이 가장 좋습니다.
  • 제 경험에 따르면 Oracle용 Microsoft OLEDB 공급자가 Oracle OleDb 공급자보다 더 잘 작동했습니다.당신은 그것을 테스트해야 합니다.
    편집: 가능한 블롭 문제에 대한 Fabricio의 코멘트를 확인합니다.
  • TADO 바꾸기TAdoDataSet을 사용한 쿼리입니다.BDE에서 ADO로 앱을 변환하기 위해 TadoQuery가 생성되었지만 Borland/Codegear 권장 사항은 TadoDataSet을 사용하는 것이었습니다.
  • Oracle 연결 문자열을 다시 확인하여 네트워크 지연 시간이 없는지 확인합니다.Oracle에 연결하는 데 얼마나 오래 걸립니까?TnsPing은 얼마나 걸립니까?

몇 년 전 ADO Express의 성능 문제를 발견했습니다.

참고: ADO가 Delphi의 표준 부품이 되기 전에 Borland는 ADO Express라는 애드온으로 판매하고 있었습니다.Microsoft의 ADO(ActiveX Data Objects) COM 개체를 둘러싼 개체 래퍼일 뿐입니다.

저는 세 가지 시나리오를 테스트했습니다.

  • ADO를 직접 사용(즉, Microsoft의 COM 개체를 직접 사용)
  • ADO Express(ADO 주변의 볼랜드 개체 래퍼) 사용
  • 지정.DisableControls에서.TADOQuery전화하기 Open

나는 발견했습니다.

  • 사용하다Query.DisableControls각각의 전화를 걸기 위해.Next50배 더 빠른 속도
  • 사용하다Query.Recordset.Fields.Items['columnName'].ValueQuery.FieldByName('columnName')값를 2 더 빠르게 하기 값 조 시 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,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;

제어 사용 안 함을 사용하여 루프를 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;

최상의 성능을 위해 오픈 소스에서 오라클에 직접 액세스하는 방법을 살펴보아야 합니다.

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 버전은 의 1/4에 대해 임의로 null을 반환합니다.

실제 데이터베이스에서는 OleDB 프로바이더를 설치할 필요 없이 직접 OCI 클래스가 OleDB 프로바이더보다 2-5배 더 빠르다는 것을 알게 되었습니다.표준(대규모) Oracle 클라이언트를 설치하거나 Oracle_HOME을 설치하지 않고도 애플리케이션을 실행할 수 있는 Oracle Instant Client도 사용할 수 있습니다. 애플리케이션과 동일한 디렉토리에 dll 파일을 전송하기만 하면 작동합니다.

언급URL : https://stackoverflow.com/questions/369187/bde-vs-ado-in-delphi

반응형