SQL - mostrando o progresso em um sql

Top  Previous  Next

Mostrando progresso de uma SQL

 

Algumas pessoas estavam interessadas em saber como apresentar o progresso

de um TQuery enquanta ele esta sendo aberto (ou executada, no caso de um

INSERT / UPDATE / DELETE).

 

A tecnica que vou demostrar nao apenas serve para o proposito procurado,

mas tambem serve para mostrar o progresso de diversas outras atividades que

o BDE executa, como:

 

* Criacao de tabelas

* Criacao de indices para tabelas

* Reestruturacao de tabelas

* Execucao de queries (ja comentado)

* alguma outra coisa que no momento nao me ocorre... :))

 

Importante:

 

1) No meu exemplo, estou usando o Delphi 3.02. Caso seu Delphi seja de

uma versao menor, vc devera ter um trabalho extra para repor a classe

TBDECallback. Acredito que seja possivel fazer uma rotina que funcione em

Delphi 1, mas que com certeza dara um certo trabalhinho, ah, isso dara... 

:-/

 

2) Ate agora so usei esse codigo com tabelas Paradox, mas realmente

acredito que ele venha a funcionar com base de dados Interbase, Oracle,

etc...

 

3) Nao sei se com o uso do Opus, Apollo ou qualquer outro substituto do

BDE a tecnica ira funcionar, uma vez que nao se estaria trabalhando com o

BDE original. Talvez alguem da lista possa dar essa informacao.

 

Teoria

=====

Segundo o help do Delphi, "o TBDECallback eh um wrapper para uma funcao

de callback do BDE. Com ele eh possivel instruir o BDE para que o mesmo

execute algumas tarefas em resposta a eventos que ocorram durante uma

chamada de uma funcao do BDE. " - Fim do plagio do arquivo de help.

 

O tipo de callback depende de um parametro CBType que eh fornecido no

momento da criacao do TBDECallback. E, entre os diversos valores que o

CBType pode apresentar, existe um que muito nos interessa; o cbGENPROGRESS.

:))

Assim, vc deveria criar uma funcao de callback do tipo cbGENPROGRESS

chamada AtualizaGauge e indicar que a mesma eh que devera ser executada

"entre cada respiracao" do BDE. Na rotina AtualizaGauge, o BDE iria te

informar o percentual de progresso da tarefa .

O que voce faria nessa rotina ? Simples... atualizar o Gauge / ProgressBar.

 

Tudo muito bonito, tudo muito comovente, mas agora vamos para o lado

pratico... 

 

Pratica

======

Para que o BDE possa informar o progresso da tarefa, ele precisa obter

essa informacao da base de dados que esta sendo utilizada. Acontece que,

por razoes diferentes, nem sempre ele eh capaz de saber o PERCENTUAL da

tarefa. Numa copia de registros de uma tabela para outra, ele pode saber

que ja foram copiados 270 registros, mas nao saber que esse esforco

representa 36 % de todos os registros que serao copiados.

 

Assim sendo, na funcao de callback que sera criada, receberemos um

parametro do tipo pCBPROGRESSDesc, que por sua vez eh um ponteiro para uma

estrutura que contem duas informacoes:

 

iPercentDone => percentual do servico realizado

szMsg => texto descrevendo o progresso do servico.

 

Como usar esses parametros ? Simples: sempre que o iPercentDone for

negativo, voce devera considerar o texto descrito no campo szMsg. Se for

igual ou maior que zero, entao vc devera considerar o valor do proprio

iPercentDone.

 

Uma boa noticia para quem se preocupa com as mensagens que aparecem em

ingles, quando se quer na verdade mostra-las em portugues: a mensagem

fornecida por szMsg devera sempre aparecer no formato <mensagem><:><valor>

.....

Exemplo:

 

Records copied: 170

 

Assim, voce pode procurar pelos dois pontos ":" e pegar o valor que vem a

seguir para montar sua propria informacao em portugues.

 

Pessoalmente, ate agora nunca obtive um iPercentDone positivo. Li no

newsgroup da Borland que poucas bases de dados eram capazes de informar o

real percentual para o BDE. Se nao me engano, o Sybase era um deles... NAO

ESTOU CERTO DISSO.

 

Vamos para um exemplo pratico ? Crie um projeto novo, e coloque um: 

TQuery, TButton, TProgressBar e TLabel.

Sua query deve ser montada para abrir uma tabela razoavelmente grande, de

modo que a operacao de abertura demore um pouco.

 

Agora vamos aos codigos:

 

1) Acrescente a unit BDE no seu USES da unit.

2) Acrescente algumas declaracoes na declaracao do seu Form:

==============================

type

  TForm1 = class(TForm)

    ... (bla bla bla)

  private

    { Private declarations }

    FCBPROGRESSDesc  : pCBPROGRESSDesc;

    FProgressCallback: TBDECallback;

    function GetDataCallback(CBInfo: Pointer): CBRType;

  public

    { Public declarations }

  end;

==============================

 

No evento OnCreate do seu Form:

==============================

procedure TForm1.FormCreate(Sender: TObject);

begin

  FCBPROGRESSDesc   := AllocMem(SizeOf(CBPROGRESSDesc));

  FProgressCallback := TBDECallback.Create(Self, Query1.Handle, cbGENPROGRESS, 

                       FCBPROGRESSDesc, SizeOf(CBPROGRESSDesc), GetDataCallback, True);

end;

==============================

 

Percebam que no segundo parametro do Create do callback, eu coloquei

Query1.Handle.

Caso voce queira usar isso numa TTable, coloque Table1.Handle.

E se quiser que essa funcao de callback seja chamada para todos os

"progressos" de qualquer componente DataSet, voce deixa esse parametro como

NIL.

 

 

No evento OnDestroy do Form:

==============================

procedure TForm1.FormDestroy(Sender: TObject);

begin

  FProgressCallback.Free;

  FreeMem(FCBPROGRESSDesc, SizeOf(CBPROGRESSDesc));

end;

==============================

 

E agora, a tao falada funcao de callback:

=========================================

function TForm1.GetDataCallback(CBInfo: Pointer): CBRType;

begin

  Result := cbrCONTINUE;

  with pCBPROGRESSDesc(CBInfo)^ do

  begin

    if iPercentDone < 0 then

    begin

      Label1.Caption := szMsg;

      Label1.Refresh;

      ProgressBar1.StepIt; {Apenas para ficar rodando o gauge}

    end

    else

      ProgressBar1.Position := iPercentDone;

  end;

end;

==============================

 

Agora eh so executar a query no clicar do botao e curtir o visual... :))

 

IMPORTANTE !!!!!!

 

Caso voce receba uma mensagem de erro informando que nao foi possivel

inicializar o BDE (o que provavelmente acontecera, pois voce esta criando

uma funcao de callback do BDE, quando ate entao nenhuma tabela havia sido

aberta), va no DPR do seu projeto (Menu View -> Project Source) e faca o

seguinte:

 

1) Acrescente a unit BDE no uses do projeto.

2) Acrescente a instrucao 

 

DbiInit(nil);

 

apos a instrucao Application.Initialize;

 

Isso deve resolver o problema.

 

Bom, nao vou me alongar mais, porque senao essa mensagem vai ficar maior do

que ja esta...

Espero que tenha contribuido para a solucao desse problema de mostar

progresso de uma query. Qualquer duvida mandem mensagem.