segunda-feira, 18 de outubro de 2010

Corrigindo problemas de instalação do Borland Data Provider For FireBird

Em alguns casos, o Borland Data Provider For Firebird têm apresentado problemas de instalação, abaixo segue algumas dicas para tentar auxiliar na resolução dos mesmos:

Verificar se o D8 está atualizado com Update Pack#2;
Se o BDP For Firebird foi instalado 'antes' do D8 ter sido atualizado,desinstalar, atualizar o D8 e após isso instalar o BDP For Firebird novamente;
Efetuar testes diretamente via 'Data Explorer' a fim de verificar se a conexão ocorre sem problemas;
Copiar o arquivo 'FirebirdSql.Data.Bdp' para a pasta:
C:\Arquivos de programas\Arquivos comuns\Borland Shared\BDS\Shared\Assemblies\2.0

5. No projeto, acesse 'Project Manager' | 'References' e adicione a referência ao 'Firebirdsql.Data.Bdp'

Como usar a cláusula UNION em um Query

O uso do componente TQuery gera muitas vantagens e economiza muitas linhas de programação. Mas muitas vezes nos deparamos com situações que parecem não ser resolvidas com sentenças SQL. Vejamos um exemplo:

Você possui 2 tabelas (VendasExternas e VendasInternas) e deseja fazer um resumo de todas as vendas de um vendedor chamado Carlos. Se você usar a sentença

SELECT Nome, Valor FROM VendasExternas, VendasInternas
WHERE Nome = 'Carlos'

você vai obter como resultado uma query com 4 campos (Nome, Valor, Nome_1 e Valor_1) e um resultado bem confuso para ser manipulado.

Para resolver o problema, você poderá usar a sentença

SELECT Nome, Valor FROM VendasExternas
WHERE Nome = 'Carlos'
UNION ALL
SELECT Nome, Valor FROM VendasInternas
WHERE Nome = 'Carlos'

A sentença acima pede para que sejam identificados as vendas de Carlos na tabela VendasExternas, as vendas de Carlos na tabela VendasInternas e que o resultado da primeira seja unido com o resultado da segunda produzindo uma query com apenas 2 colunas.

Mostrando progresso de uma SQL

A tecnica apresentada 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 <:> .....
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 operação de abertura demore um pouco.

Agora vamos aos codigos:

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

2) Acrescente algumas declarações na declaração 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 é só 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.

Verificando atributo do arquivo

Para verificar um atributo de um arquivo, crie uma variável do tipo word, por exemplo, Attributes. Depois, atribua a esta variável o valor retornado por FileGetAttr. Ex.:


var
Attributes: Word;
begin
Attributes := FileGetAttr( 'nomedoarquivo' );
// Supondo 4 CheckBoxe's, 1 para cada atributo, Ok?
CheckBox1.Checked := (Attributes and faReadOnly) = faReadOnly;
CheckBox2.Checked := (Attributes and faArchive) = faArchive;
CheckBox3.Checked := (Attributes and faSysFile) = faSysFile;
CheckBox4.Checked := (Attributes and faHidden) = faHidden;
end;

quarta-feira, 1 de setembro de 2010

Obtendo a data do último dia do mês, ou último dia útil, de uma data informada

Function LastDayOfMonth (Data : TDateTime; lSabDom : Boolean) : TDateTime;
var
    Ano, Mes, Dia : word;
    AuxData : TDateTime;
    DiaDaSemana : Integer;
begin
    AuxData := FirstDayOfMonth (NextMonth (Data), False) - 1;
    if lSabDom Then begin
        DecodeDate (Auxdata, Ano, Mes, Dia);
        DiaDaSemana := DayOfWeek (AuxData);
        if DiaDaSemana = 1 Then
            Dia := Dia - 2
        else
        if DiaDaSemana = 7 Then
            Dec (Dia);
        AuxData := EnCodeDate (Ano, Mes, Dia);
    end;
    LastDayOfMonth := AuxData;
end;

Fazendo cálculo de horas

var
    hora1: TDateTime;
    hora2: TDateTime;
    total: TDateTime;
begin
    hora1 := StrToTime(Edit1.Text);
    hora2 := StrToTime(Edit2.Text);
    total := Hora2 - Hora1;
    Label1.Caption := FormatDateTime('hh:nn:ss',total);
end;

Descobrir se uma data é fim do mês

Esta função retorna true se a data passada como parâmetro é fim de mês. Retorna false caso contrário.

Inclua na seção uses: SysUtils

function tbFimDoMes(const Data: TDateTime): boolean;
var
  Ano, Mes, Dia: Word;
begin
  DecodeDate(Data +1, Ano, Mes, Dia);
  Result := Dia = 1;
end;

Curso de Delphi: 7.Consultas SQL