[Delphi] Tabela temporária com ClientDataSet – Final

[Delphi] Tabela temporária com ClientDataSet - FinalBom, hora de fechar o tema sobre tabelas temporárias no Delphi! Espero que nos dois primeiros artigos você tenha compreendido, de forma satisfatória, como trabalhar com tabelas temporárias utilizando ClientDataSet. Este último artigo apenas apresenta algumas observações relacionadas a tabelas temporárias que podem ser úteis durante o desenvolvimento.

 

Tabelas temporárias também podem ser criadas em tempo de execução, bem como a definição dos seus campos. Observe o exemplo abaixo, onde instancio um TClientDataSet e adiciono três campos de diferentes tipos de dados (integer, string e float):

var
  ClientDataSet1: TClientDataSet; // a unit DBClient deve ser declarada na "uses"
begin
  ClientDataSet1 := TClientDataSet.Create(nil);
  ClientDataSet1.FieldDefs.Add('CODIGO', ftInteger);
  ClientDataSet1.FieldDefs.Add('DESCRICAO', ftString, 60);
  ClientDataSet1.FieldDefs.Add('VALOR', ftFloat);
  ClientDataSet1.CreateDataSet;
end;

 

E campos agregados também! Caso você não conheça, campos agregados servem para realizar cálculos em uma determinada coluna do ClientDataSet. No exemplo a seguir, criei um campo agregado para somar automaticamente o valor total de todos os itens da tabela.

with ClientDataSet1.Aggregates.Add do
begin
  AggregateName := 'Valor Total';
  Expression := 'SUM(TOTAL)';
  Active := True;
end;
ClientDataSet1.AggregatesActive := True;

 

Muitas vezes pode ser necessário “esvaziar” a tabela temporária, como por exemplo, no botão “Limpar” ou “Cancelar” de um formulário. Essa instrução pode ser realizada com apenas uma linha de código:

ClientDataSet1.EmptyDataSet;

 

Barbada, não?
Além disso, tabelas temporárias também permitem a navegação entre os registros na memória utilizando os métodos tradicionais já conhecidos:

ClientDataSet1.First; // move para o primeiro registro
ClientDataSet1.Last;  // move para o último registro
ClientDataSet1.Prior; // move para o registro anterior
ClientDataSet1.Next;  // move para o próximo registro

 

Outro recurso bastante interessante do ClientDataSet é o clone do conjunto de dados. Através do comando CloneCursor é possível copiar os dados de um ClientDataSet para outro, e então manipulá-los de maneira independente.

// ClientDataSet2 "clona" os dados de ClientDataSet1
ClientDataSet2.CloneCursor(ClientDataSet1, True);

 

Agora, imagine que estamos utilizando uma tabela temporária para gravar itens de uma venda. Não é interessante que produtos repetidos sejam inseridos na tabela, concorda? Afinal, se o código do produto fizer parte da chave primária, ocorrerá um erro ao gravar os itens da venda.
Para resolver isso, podemos utilizar a função Locate e controlar a inserção de itens repetidos:

if ClientDataSet1.Locate('COD_PRODUTO', edtCodProduto.Text, []) then
  ShowMessage('Este produto já foi adicionado!')
else
  // grava o registro na tabela

 

Para filtrar os registros, não há segredo. No exemplo abaixo, tenho um componente do tipo TEdit chamado edtPesquisa, e permito o filtro da descrição de um registro na tabela temporária conforme o conteúdo digitado no campo:

ClientDataSet1.Filter := 'DESCRICAO like ' + QuotedStr(edtPesquisa.Text + '%');
ClientDataSet1.Filtered := True; // ativa o filtro

 

Só lembrando que o símbolo de porcentagem permite que todos os registros que iniciem com a palavra digitada em edtPesquisa sejam encontrados. Por exemplo, se o usuário digitar a letra “A”, todas as descrições que começam com essa letra serão filtradas.
Mas atenção: não esqueça de desativar o filtro quando for necessário trabalhar com todos os registros da tabela temporária.

 

Bom, pessoal, espero que tenham gostado dessa série de artigos sobre tabelas temporárias, e que de alguma forma venha a ser útil pra vocês! Boa sorte no trabalho e até breve!


Confira os outros artigos:

Tabela temporária com ClientDataSet – Conceito
Tabela temporária com ClientDataSet – Prática
Tabela temporária com ClientDataSet – Final


 

Compartilhe!
Share on FacebookTweet about this on TwitterShare on LinkedInShare on Google+Pin on PinterestEmail this to someone

36 comentários

  1. Eu já programo em Delphi há algum tempo e sempre tive o interesse em trabalhar com banco de dados, porém sempre achei um pouco complicado. Este artigo foi muito interessante e didático que percebi que é simples e de certa forma fácil trabalhar com banco de dados no Delphi. Vou estudar este artigo e implementar nos meus aplicativos.

    Obrigado.

    Júlio.

  2. ola tem como usar um if dentro do locate? exemplo quero varre os registro de encontro o campo diferente de nulo.. mas nao tenho valor certo..

  3. André, tenho utilizado com sucesso tabelas temporárias com ClientDataset.
    Uma dúvida: Como acrescento um novo campo após a tabela ter sido utilizada ?
    Sempre que necessito um erro é gerado.

    1. Olá, Álvaro! Após a criação do ClientDataSet não é possível adicionar um novo campo, já que essa operação altera a estrutura interna dos Fields. Sendo assim, para criar um novo campo, é necessário remover todos os campos existentes e adicioná-los novamente:
      ClientDataSet2.Close;
      ClientDataSet2.FieldDefs.Clear;
      // adicione todos os campos anteriores e o campo novo
      ClientDataSet2.CreateDataSet;

      Espero que lhe ajude! Abraço!

  4. Obrigado André. Consegui fazer em tempo de desenho mas é muito mais difícil, o ideal é excluir o ClientDataSet e iniciar o processo mesmo.

    Seu exemplo é uma das vantagens de criação de componentes ( ou algumas propriedades ) por código.

    Muito obrigado e um abraço.

  5. Boa tarde.

    Como faço após clonar todos os registros grava-los fisicamente na tabela de dados?
    Desde já, agradeço a atenção.

    1. Olá, Valtencir. Para gravar os registros que estão na tabela temporária, basta iniciar um loop e percorrê-los um a um, gravando-os no banco de dados. Vou enviar um exemplo de código no seu e-mail, ok? Abraço!

  6. Boa noite André.
    Agradeço muito sua atenção, vou analisar o e-mail e implementar o que você me enviou.

    Grande abraço.

  7. Boa Tarde André. Explicação muito boa sobre tabela temporária. Entretanto tenho uma dúvida. Veja: Criei um campo aggregates para poder somar alguns valores de acordo com as vendas. Esta somando normal, porém a mascara que inseri no campo aggregates não funciona ou seja o valor da moeda do corrente ‘R$’ não é mostrado. Pesquisei na maioria dos fóruns Delphi e não há uma explicação. Vi alguns tutos ensinado por linha de código em evento, mas penso que deve ter algum modo pela propriedades Objecto Inspector.

    Obrigado
    Abraço.

    1. Olá, Sócrates!
      As versões do Delphi anteriores que a versão 2010 contém um bug na formação de campos Aggregates. Mesmo colocando máscaras ou marcando a propriedade “currency” como True, o número não é formatado. Neste caso, uma alternativa é formatar o número no próprio código fonte:
      Edit1.Text := FormatFloat('R$ ###,###,##0.00', ClientDataSet1.FieldByName('CAMPO_AGGREGATE').Value);

      Segundo alguns sites, também há uma opção de corrigir esse bug do Delphi. Na unit “DB.pas”, no método “TAggregateField.GetText”, basta substituir a linha:
      if FResultType in [ftFloat, ftCurrency] then
      por:
      if FResultType in [ftFloat, ftCurrency, ftFMTBcd] then
      Lembrando que, se essa alteração for feita, a unit “BD.pas” deve ser recompilada para que as alterações entrem em vigor.

      Abraço!

  8. Bom dia André,
    Muito obrigado pela atenção. Optei pela primeira opção e deu certo. Substitui o Edit por um Label e alcancei o resultado esperado. Agradeço a explicação e solução postada.

    Abraço!

    1. Hello, Alfredo!

      Unfortunately, I don’t speak Spanish. If you’re able to speak English, maybe I could help you through email.
      Let me know if it’s possible, ok?
      Regards!

  9. Bom dia André L. Celestino

    Eu admiro as pessoas que tem o dom de ensinar sem cobrar, simplesmente pelo prazer de ensinar parabéns!. André, eu acabei de me formar em Tecnologia em Análise e Desenvolvimento de Sistemas acho que até um pouco tarde pela minha idade. Mas tenho um sonho e quero me tornar programador em Delphi linguagem que escolhi para desenvolver mas não tenho muita prática, poderia me dar uma dica, para seguir.

    1. Olá, José Roberto!

      Em primeiro lugar, não se preocupe com a sua idade! Nunca é tarde para adquirir novos conhecimentos!
      José, eu não sei exatamente qual é o seu nível de habilidade em lógica de programação, mas eu posso lhe encaminhar algumas apostilas de Delphi para iniciantes. Vou entrar em contato para mais detalhes.

      Abraço!

  10. me foi muito util, igual a varias outras coisas q vc posta! XD
    eu acho q seria legal republicar essas materias do clientdataset mas com firedac, basicamente so precisa mudar uma propriedade como true do query, um usuario no forum quebrou a cabeca com isso, e eu acabei descobrindo, a sugestao é basicamente para q com isso aumente a documentacao do firedac, e uma funcionalidade muito importante para quem nao teve contato com firedac é a questao de trabalhar com dados em memoria, enfim, fica aqui a sugestao!

    1. Olá, Conde!
      Tem razão, ainda preciso elaborar alguns artigos abordando o FireDAC. Atualmente há poucos materiais na internet sobre essa tecnologia.
      Assim que possível, trabalharei nesse tema.
      Abraço!

  11. André, bom dia.
    Parabens pelo conteúdo do artigo, de todos q procurei o seu realmente foi mto esclarecedor.
    Pergunto: É possível trabalhar com colunas definidas como PK( primary key )?
    Como seria a definição?
    Obg. e sucesso.

    1. Olá, Marco!
      Ótima pergunta!
      É possível trabalhar com o comportamento parcial de uma Primary Key, ou seja, podemos definir um campo (Field) como obrigatório alterando a propriedade Required do campo para True. Entretanto, não é possível definir uma coluna como Unique (que não pode se repetir). Neste caso, você terá que controlar esse comportamento manualmente, checando se o valor já existe na tabela antes de gravar.

      Abraço!

  12. Uma pequena correção no comentário acima. É possível usar coluna com Unique em um TClienteDataSet. Basta ir na propriedade IndexDef do ClienteDataSet criar um novo index, na propriedade Fields do index coloque o campo que você deseja que não se repita e marque as opções ixPrimary e ixUnique. Escolha um nome para o index e atribua esse mesmo nome na propriedade IndexName no ClienteDataSet.

    1. Olá, Felipe, tudo bem?
      Muito obrigado pela contribuição! Eu sinceramente desconhecia essa forma de atribuir o comportamento de valor único para uma coluna de um TClientDataSet! Bem interessante!
      @Marco, confira essa dica do Felipe.

      Abraços!

  13. Bom dia, André !
    Primeiramente, parabéns pelo blog e estou sempre aqui dando uma olhada.
    “Segundamente”, poderiam tentar me ajudar com o seguinte erro que ocorre quando executo o comando ClientDataSet1.ApplyUpdates(0)
    —————————
    Project Project1.exe raised exception class TDBXError with message ‘Table ‘gaucho.lista’ doesn’t exist’.
    —————————

    Acontece que a tabela, no banco, está com letras maiúsculas, gaucho.LISTA seria o certo.

    Se executo uma query/comando manual por um TSQLDataSet ou TSQLQuery o banco atualiza normalmente.
    Se mudo o nome da tabela para letras minúsculas também funciona.

    Utilizo o Quarteto: TSQLConnection + TSQLDataSet + TDataSetProvider + TClientDataSet

    1. Boa noite, André !
      Obrigado pela atenção.

      Com relação a dica que me deste não há este item no parms do meu TSQLConnection…

    2. bom dia, André !

      Desculpe, esqueci de dizer que estou tentando conexão com MySql…

  14. Bom dia André.
    Excelente, aprendi mais duas usar like e % em um filtro, nunca imaginei, da pra usar containing também?
    Valeu mesmo.

    1. Olá, Gerson!
      Opa, que bom que aprendeu algo com o artigo!
      O Containing não está disponível na propriedade Filter justamente porque o like e o símbolo “%” já dão conta do recado!

      Abraço!

  15. Carissimo Andre, adorei tudo que escreveu, mas tenho problema já bem antigo. Sonho diariamente em criar uma tela de qualquer coisa que me permita criar desde o ClientDataset ligado a uma dsp ligado a uma query, tudo em tempo de execução, com campos calculado e campos aggregates. No entanto, até o momento só cheguei a obter resultado até o campos calculado, só me falta o aggregate, o maldito se nega a funcionar.

    Fiz assim

    Q:=TZQuery.Create(Self)
    D:=TDatasetProvider.Create(Self);
    C:=TClientDataset.Create(Self);
    ...
    ...
    Var
      campo:TField;
    Begin
      Campo:=TWideStringField.Create(Self);
      With Campo do begin
        FieldName   :='BancoNome';
        FieldKind   :=fkData;
        Size        :=30;
        Dataset     :=C;
      end;
      Campo:=TIntegerField.Create(Self);
      With Campo do begin
        FieldName   :='Doc';
        FieldKind   :=fkData;
        Dataset     :=cdFinanceiro;
      end;
      Campo:=TFloatField.Create(Self);
      With Campo do begin
        FieldName   :='SomaAberto';
        FieldKind   :=fkInternalCalc;
        Dataset     :=C;
      end;

    Até aqui está tudo funcionando, só tem um detalhe, estou usando CommandText.
    Daqui pra frente, só falta dizer que quero adicionar o AggregateField, já usei diversas forma, já perdi meses tentando descobrir a funcionalidade disso, mas até agora nada.
    A idéia geral é não usar nenhum componente em tempo de projeto.

    Se puder me dar uma pequena dica, agradeço
    Marcelo

  16. Andre, mesmo não tendo respondido, agradeço assim mesmo e informo que depois meses tentando fazer a operação acima, acabei descobrindo sozinho.

    Se alguem tiver a mesma dúvida, hoje posso instruir
    abraço a todos

    1. Olá, boa tarde, Marcelo!
      Peço desculpas pela demora para responder o seu comentário, mas, ao mesmo tempo, fico feliz que já tenha encontrado a solução.
      Neste mesmo artigo, apresento um exemplo de como criar um campo Aggregate em tempo de execução. Foi dessa forma que você criou?
      Se puder colaborar com a sua solução, a comunidade Delphi ficará agradecida! 🙂

      Apenas para aproveitar o ensejo, você pode criar a sua própria rotina de criação de Fields para reduzir a repetição de código. Por exemplo:

      procedure CriarField(DataSet: TClientDataSet;
        const Nome: string; const Tamanho: integer;
        const Tipo: TFieldKind);

      E então, você chamaria dessa forma:

      CriarField(C, 'BancoNome', 30, fkData);

      Abraço!

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Preencha o campo abaixo * Time limit is exhausted. Please reload CAPTCHA.