Olá, leitores! Esse artigo é a continuação do tema sobre tabelas temporárias com ClientDataSet no Delphi. No artigo anterior, apresentei o conceito, vantagens e um exemplo de cenário no qual uma tabela temporária pode ser utilizada para evitar inconsistências. Após a teoria, finalmente vamos partir para a prática! Criaremos uma tabela temporária utilizando o mesmo exemplo de cenário mencionado no primeiro artigo!
TClientDataSet
No artigo anterior, lembre-se que temos a seguinte tabela de itens da venda:
1 2 3 4 5 6 |
-- Tabela ITENS COD_VENDA (chave estrangeira referenciando a tabela VENDAS) COD_PRODUTO QTDE VALOR TOTAL |
Nosso objetivo será criar uma tabela temporária para armazenar os itens da venda em memória, e somente quando o usuário clicar pra gravar a venda é que estes itens serão, de fato, inseridos no banco de dados.
Para criar uma tabela temporária, vamos utilizar o componente TClientDataSet
, disponível na paleta Data Access do Delphi. Adicione-o no formulário, altere o nome para cdsTemporario
e dê dois cliques para abrir o Fields Editor (Editor de Campos). Essa janelinha é onde definiremos os campos da tabela temporária.
Para adicionar um novo campo, clique com o botão direito dentro dessa janelinha e selecione a opção New Field.
Uma nova janela será aberta para preencher as propriedades do campo que será adicionado.
No nosso caso, adicionaremos 4 campos:
Código do Produto:
Name: COD_PRODUTO
Type: Integer
Field Type: Data
Quantidade:
Name: QTDE
Type: Integer
FieldType: Data
Valor do Produto:
Name: VALOR
Type: Float
FieldType: Data
Total (quantidade multiplicada pelo valor):
Name: TOTAL
Type: Float
FieldType: Data
Por quê não adicionamos a descrição do produto?
Caro leitor, se você já trabalhou com master/detail alguma vez ou tem noções de normalização de dados, sabe que não devemos gravar a descrição do produto na tabela de itens da venda, já que temos o código do produto como chave estrangeira. Se adicionarmos a descrição do produto, teremos valores duplicados no banco de dados (na tabela Produtos e na tabela Itens da Venda), e isso é desnecessário. Apenas com o código do produto podemos facilmente buscar a descrição através de um relacionamento entre tabelas (inner join).
Quando terminar de adicionar os itens, feche o Fields Editor. Em seguida, clique com o botão direito no cdsTemporario
e selecione a opção Create DataSet.
Pronto! A partir de agora já temos uma tabela temporária criada! A seguir, veja que a manipulação de dados também é bem simples.
Usando a tabela temporária
Para inserir um registro na tabela temporária, utilizaremos o método Append
para colocá-la em modo de inserção e o método Post
para gravar o registro, tal como se estivéssemos trabalhando com uma tabela física. Considerando que temos componentes TEdit
para cada campo da tabela temporária, o código de inserção ficaria da seguinte forma:
1 2 3 4 5 6 7 8 9 10 11 |
var Total: real; begin cdsTemporario.Append; cdsTemporario.FieldByName('COD_PRODUTO').AsInteger := StrToInt(edtCodigo.Text); cdsTemporario.FieldByName('QTDE').AsInteger := StrToInt(edtQtde.Text); cdsTemporario.FieldByName('VALOR').AsFloat := StrToFloat(edtValor.Text); Total := StrToInt(edtQtde.Text) * StrToFloat(edtValor.Text); cdsTemporario.FieldByName('TOTAL').AsFloat := Total; cdsTemporario.Post; end; |
Observe que não há segredo algum! Para visualizar as inserções, adicione um componente TDBGrid
e um TDataSource
no formulário e faça as ligações entre os três componentes para exibir os dados.
Caso seja necessário alterar um registro da tabela temporária, basta utilizar o método Edit
para colocar a tabela em modo de edição (alteração), ao invés do Append
. Ah, e lembre-se de utilizar o Post
para gravar as alterações. Por fim, se for necessário deletar um registro da tabela temporária, utilize o método Delete
:
1 |
cdsTemporario.Delete; |
Persistindo os dados no banco
Muito simples! Mas antes disso, precisamos obter o código da venda para que seja possível gravar os itens da venda, concorda? Na verdade, isso depende de qual banco de dados você usa e como você definiu a tabela. Se o código da venda for um campo autoincremento, ele pode ser obtido através de uma função de retorno (como o RETURNING do Firebird ou @@IDENTITY do SQL Server).
Por outro lado, se o código da venda é preenchido pelo sistema, podemos utilizar a função MAX em uma SQL, que retorna o valor máximo de um campo em uma tabela. No nosso caso, seria o campo Código da venda recém inserida:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var CodigoVenda: integer; // variável para armazenar o código da venda begin // limpa a instrução SQL Query1.SQL.Clear; // adiciona uma instrução SQL com a função MAX Query1.SQL.Add('Select MAX(COD_VENDA) as CodigoVenda from VENDAS'); // abre a Query Query1.Open; // obtém o código da venda CodigoVenda := Query1.FieldByName('CodigoVenda').AsInteger; // fecha a Query Query1.Close; end; |
Pois bem, considere que temos outro ClientDataSet (chamado cdsItensVenda
) que representa a tabela física de itens da venda no banco de dados. Para “copiar” os dados da tabela temporária para a tabela física, basta realizar um laço de repetição (loop) e gravar os itens, um a um. Complementando o código-fonte acima, nossa sintaxe finalmente ficará da forma abaixo.
Para facilitar a visualização, quebrei algumas linhas do código, ok?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
var CodigoVenda: integer; begin { aqui estaria o código de gravação da venda } Query1.SQL.Clear; Query1.SQL.Add('Select MAX(COD_VENDA) as CodigoVenda from VENDAS'); Query1.SQL.Open; CodigoVenda := Query1.FieldByName('CodigoVenda').AsInteger; Query1.Close; cdsTemporario.First; // move para o primeiro registro da tabela temporária while not (cdsTemporario.EOF) do // laço de repetição begin cdsItensVenda.Append; // coloca a tabela física em modo de inserção // copia os valores da tabela temporária para a tabela física cdsItensVenda.FieldByName('COD_VENDA').AsInteger := CodigoVenda; cdsItensVenda.FieldByName('COD_PRODUTO').AsInteger := cdsTemporario.FieldByName('COD_PRODUTO').AsInteger; cdsItensVenda.FieldByName('QTDE').AsInteger := cdsTemporario.FieldByName('QTDE').AsInteger; cdsItensVenda.FieldByName('VALOR').AsFloat := cdsTemporario.FieldByName('VALOR').AsFloat; cdsItensVenda.FieldByName('TOTAL').AsFloat := cdsTemporario.FieldByName('TOTAL').AsFloat; cdsItensVenda.Post; // grava o item da venda // deleta o registro da tabela temporária // isso fará com que o próximo registro seja lido cdsTemporario.Delete; end; cdsItensVenda.ApplyUpdates(0); // persiste os itens no banco de dados end; |
Em poucas linhas gravamos a venda e todos os itens dela em um mesmo método!
É bem mais rápido e seguro, não é?
Clique aqui e baixe um exemplo de tabela temporária com ClientDataSet desenvolvido em Delphi 7. Neste exemplo, procurei incrementar mais algumas funções (como o recurso de Aggregates), e adicionei um campo “Descrição” para aprimorar a compreensão de tipos de dados no ClientDataSet. Analise o código e teste o exemplo. Caso houver dúvidas, não hesite em entrar em contato!
Antes que eu me esqueça, tabelas temporárias não servem exclusivamente para o cenário apresentado acima. Você pode utilizar tabelas temporárias para várias finalidades em diversas situações. O importante é compreender o conceito e saber aplicá-lo quando necessário.
Por hoje é só, leitores! Grande abraço!
Confira os outros artigos:
Tabela temporária com ClientDataSet – Conceito
Tabela temporária com ClientDataSet – Prática
Tabela temporária com ClientDataSet – Final
Vale lembrar que você também pode fazer com que o TClientDataSet trabalhe automaticamente com o banco de dados ligando TQuery > TDataSetProvider > TClientDataSet > TDataSource. Vale um artigo sobre isso, André?
Interessante o artigo… Como estou iniciando no em programação Delphi já trabalhei desta forma de gravar e depois sair alterando .. kkk .Parabéns André.
Como seria a alteração de dados?
Olá, Fred! Para colocar a tabela temporária em modo de edição, basta utilizar o comando Edit ao invés de Append, e para gravar é o mesmo comando: Post!
Somente isso?
Basicamente sim, Frederico. Você ainda pode utilizar outros métodos para manipular os dados, como Delete (exclusão), Locate (localização), Filter (filtro), Refresh (atualização), além dos comandos Append, Edit e Post já mencionados.
Realmente usar estas tabelas é fantástico, porém estou esbarrando num problema. Estou alimentando uma tabela dessas com mais de 5.000 registros (vindos de outra tabela).
Um dos campos desta tabela é criado manualmente, trata-se de um campo tipo BOOLEAN que o usuário vai usar para marcar registros específicos e fazer algo com eles.
Só que a alimentação desses 5 mil registros nesta tabela temporária está DEMORANDO DEMAIS. Existe uma forma de alimentar mais rapidamente essas tabelas temporárias?
Olá, Ismael! Depende de qual forma você está alimentando os dados na tabela temporária. Talvez você pode “clonar” os dados, e isso leva bem menos tempo. De qualquer forma, vou entrar em contato com você por e-mail!
André, estou com um problema em uma DBGrid que está ligada a um data source que por sua vez está ligado à um ClientDataSet, queria saber se você saberia como limitar a quantidade de caracteres de uma célula de uma DBGrid, por exemplo no campo Código da DBGrid só 2 caracteres, no campo Quantidade somente 6, e por assim vai..
Olá, Eduardo. O limite de caracteres deve estar definido no ClientDataSet, e não na DBGrid. Por exemplo, se você criou um campo no ClientDataSet do tipo string de 20 caracteres, então o DBGrid irá automaticamente respeitar esse limite e não permitirá que mais de 20 caracteres sejam digitados na célula. Ok?
André, por exemplo tenho um SQLConnection , um SQLDataSet, um DataSetProvider, um ClientDataSet e um Datasource, que está ligado a um banco de dados, então o que está exposto no DBGrid vem do banco, mas quando vai adicionar no DBGrid no campo Quantidade, ele não limita os caracteres e se for digitado além do permitido ele da um erro no Banco de Dados..
Boa questão, Eduardo. No caso de campos do tipo string, a DBGrid obedece a limite de caracteres que foi definida paro o campo. Já para campos do tipo inteiro (como a Quantidade, no seu caso), eu recomendo que você crie uma máscara em tempo de execução para limitar a entrada do usuário, como por exemplo:
ClientDataSet1.FieldBYName('Quantidade').EditMask := '000';
gostaria de saber como eu faço para que o clientDatSet fique no estado True, porque toda vez que abro o delphi tenho que alterar essa opção..grato abraço….
Olá, getulio. A princípio, só pelo fato de você ajustar a propriedade Active como True em tempo de projeto já seria o suficiente para mater o ClientDataSet sempre ativo, mesmo reiniciando o Delphi.
André meu compatriota,Parabéns pelo seu trabalho e em compatilhar seu conhecimento!Sou estudante de S.I e estou começando à aprender Delphi.Queria se possível sua ajuda:
Estou desenvolvendo um aplicativo pequeno de estoque e estou quebrando a cabeça
com a tabela produto e venda. Queria uma ajuda sua meu guru Delphiano! No formulário “venda”, ao inserir o código do produto que é da tabela produto,
mostrasse o nome do produto, a quantidade em estoque. Dái quando inserir no campo de “qtde” da tabela venda e gravasse a venda, desse baixa automática
na tabela produto.
RESUMINDO, QUERO FAZER UMA VENDA QUE DESSE BAIXA AUTOMÁTICA DO PRODUTO EM ESTOQUE
Estou usando a paleta “Interbase” do Delphi 7 + Firebird 2.5
Olá, Walter! Agradeço pela visita e também pelo comentário. Pra ficar melhor, vou entrar em contato com você por e-mail, ok? Abraço!
Olá, André!
Agradeço aos ‘deuses’ da Informática, por encontrá-lo falando de Delphi, e explicando muito bem sobre o assunto, neste momento que tanto preciso.
Doutor, minha situação é de certa forma semelhante a do nosso companheiro Walter. Preciso desenvolver um aplicativo, onde no formulario eu escolhesse a categoria e partir daí incluisse os itens referentes a mesma, mas sem ter que ficar escolhendo toda vez a categoria antes de incluir os itens nela. Se é que me entende. Se puder entrar em contato comigo, poderei te explicar melhor. Desde já, agradeço!
Olá, Leandro. Obrigado por deixar o comentário! Vou entrar em contato com você em breve. Abraço!
André, quanta qualidade em uma explicaçao, são poucos que sabem ensinar tao facilmente assim.
Meu problema é assim: estou fazendo uma tela de venda(saida de produtos), estou usando delphi 7 + firebird 2.1, com os componentes da palheta interbase, gero uma nova venda, preencho os dados da venda, e os itens estou colocando em um DBGrid, como faço pra colocar essa tabela temporaria pra trabalhar com o DBgrid, facilitaria minha vida, nao quero colocar Edit’s para inserir itens, quero fazer isso direto em um DBgrid ou um componente com a mesma funçao.. Abraço Desde ja Agradeço!
Olá, Thiago! Obrigado pelo comentário e pelo feedback! Thiago, podemos trabalhar com tabelas temporárias em DBGrids sem problema algum! A única diferença é que os dados estarão em memória, ao invés de armazenados no banco de dados. Isso é muito útil para trabalhar com vários registros que dependem de uma chave estrangeira que ainda não foi gravada.
Cara
Eu não costumo fazer isso, mas OBRIGADO
Do fundo do meu coração, OBRIGADO
Tu não tem idéia de como vc me ajudou com esse post
Valeu ae 😀
Olá, Thobias! Fico contente que o blog tenha lhe ajudado! Abraço!
André, boa tarde!
Excelente artigo! Me ajudará bastante numa rotina que estou desenvolvendo.
Felicidades e sucesso para você!
Muito obrigado.
Boa tarde, Kleberson.
Fico bastante animado em saber que o artigo lhe ajudou.
Obrigado pelo feedback!
Abraço!
Bom dia, voce teria alguma dica de como colar colunas de excel no dbgrid delphi7,
10 registro, e cada 1 ira representa um na tabela paradox,
ou seje ficaria 10 registro em paradox.
Grato.
Olá, Júnior! Baixe e execute o exemplo “Importação de arquivo XLS/XLSX” que está nessa página:
https://www.andrecelestino.com/exemplos/
Espero que lhe ajude! Abraço!
Há anos aprendendo com o mestre Batera, haha! Estou quase formado e ainda aprendo com você. Ainda irei trabalhar com você um dia. Ta muito bem explicado, e ajuda muito quem usa tabelas temporarias.
corohsnk do forum ActiveDelphi.
Abraço
Att, João Alexandre.
Fala, João Alexandre!
Obrigado pelo comentário! Quem sabe um dia nos encontramos por aí, rsrs!
Se tudo der certo, logo estarei de volta no ActiveDelphi. Abraço!
Boa tarde, primeiramente gostaria de parabenizá-lo pelo excelente artigo me ajudou muito no meu problema fiz todo passo a passo. Funcionou beleza por uma exceção, o momento de mostrar no DBgrid. A minha implementação carrego os dados no DBgrid no click do botão. Acredito eu que os dados são carregados haja vista que ao inserir novos dados e clicar no botão novamente uma nova linha no DBGrid é Gerada, apenas não consigo visualizar no DBGrid. Desde já muito obrigado!
Boa noite, Júnior! Se os dados não estão sendo exibidos na DBGrid, talvez ela não está conectada na tabela temporária (ClientDataSet). Verifique a propriedade DataSource da DBGrid, bem como a propriedade DataSet do DataSource. Abraço!
Estou Com Um Problema ao Implementar seu Exemplo. ele funciona direitinho, Porém não consigo carregar os Dados no DBGrid.
Atenciosamente.
Júnior Lira
Amigo, sou iniciante em programacao e estou com sérias dificuldades. Gostaria de saber se voce poderia passar um email e se eu poderia enviar uma dúvida pra voce.
meu email é [email protected]
Desde já agradeco
Joao
Olá, João! Assim que possível, entrarei em contato, ok?
Abraço!
Valeu, Andre pela informação do site :
a duvida posso usa esse codigo no dbnavigator?
grato
Olá, josewillamis. Sim, as tabelas temporárias podem ser utilizadas como se fossem um DataSet conectado a uma tabela do banco de dados. Portanto, basta ligar a tabela temporária em um DataSource, e este DataSource em um DBNavigator!
Abraço!
André, boa tarde.
Parabéns pelo trabalho!!!
Tenho uma duvida, preciso alimentar MemDataSet, porém quando faço receber a informação da seguinte mensagem: “Field value required”. Eu preciso alterar somente um campo de varios que tenho.
Estou fazendo dessa forma:
DM.cdsCotacao.Edit; DM.mdsCotacao.FieldByName(‘PrecoFinal’).AsFloat := DM.mdsCotacao.FieldByName(‘Preco’).AsFloat;
DM.cdsCotacao.Post;
Como devo fazer nesse caso?
Boa tarde, Lucas. Ao que parece, você está editando um DataSet e atribuindo o valor em outro, o que talvez possa estar causando o erro. De qualquer forma, vou entrar em contato por e-mail. Abraço!
Caro André. Muito boa sua explanação! Só tenho uma dúvida, no trecho :
// deleta o registro da tabela temporária
// isso fará com que o próximo registro seja lido
cdsTemporario.Delete;
A propriedade Next, também não poderia ser usada?
Qual a diferença entre as duas?
Olá, Rodrigo, tudo bem?
Ótima pergunta! Quando um registro de um DataSet é excluído (utilizando a instrução Delete), o cursor do DataSet se move automaticamente para o próximo registro, portanto, não há a necessidade de utilizar o Next. Na verdade, ao utilizar o Next, pela lógica, estaríamos pulando 1 registro.
Para ficar mais fácil, considere que temos 3 registros em um DataSet:
Registro 1 <-- Registro 2 Registro 3
O cursor do DataSet está apontando para o "Registro 1". Ao utilizar o Delete, o "Registro 1" deixa de existir e o cursor se move para o "Registro 2":
Registro 2 <-- Registro 3
Logo, se utilizarmos o Next, o cursor se moverá para o "Registro 3", ou seja, pularíamos o "Registro 2".
Vale ressaltar que essa operação só é adequada quando não for preciso utilizar os mesmos registros futuramente, já que eles serão excluídos. Caso a intenção seja apenas percorrer os registros do DataSet, o correto é utilizar o Next.
Espero que tenha ficado claro! Abraço!
Show de bola, estava usando um next e o dataset nunca chegava ao final e não saia do loop: while not cdsTemp.eof. Troquei pra delete e funcionou! Muito obrigado! VC é fera.
Oi André achei muito interessante e proveitoso o artigo e preciso de uma ajuda pois sou iniciante, quero criar uma aplicação (já estou criando em Delphi mesmo) e tenho que levá-la ao Pc de outra pessoa só que na condição de não ter nenhuma ferramenta instalada lá,(tipo o software da receita federal, você já viu ?), onde o mesmo usa arquivos Xml como banco de dados, então, o Pc da pessoa não pode ter nada instalado, simplesmente só levo meu programa e instalo nele, como se fosse qualquer um baixado da web.
Pelo amor de DEUS me ajude pois a tempos venho quebrando a cabeça e perdendo tempo em sites, blogs e outras fontes falsas de ensino.
Desde já agradeço e que DEUS o abençoe…
Olá, José Pessoa, tudo bom?
Vou entrar em contato com você para pedir mais detalhes, ok?
Abraço!
Obrigado por responder André, vamos lá : 1° – Quero muito saber como criar um programa e seu instalador, como eu já disse igual ao da ‘ Receita Federal ‘ com banco em xml (Aplicação mais leve e menor) , 2° – A aplicação que estou aprendendo a desenvolver usa como banco o MySQl 5, o ZeosLib e o Delphi que eu uso é o Rad Studio XE2 update4. Mas desculpe minha ignorância mas não adianta só saber o que tem que levar para o outro Pc e sim como faze-lo. Resumindo quero chegar no Pc do cara que não tem o MySQl 5, o ZeosLib e nem o Delphi que eu uso que é o Rad Studio XE2 update4 nem dll nenhum instalado, clicar no setup do meu programa e ele fazer o resto. Já tentei dicas com installshield wizard, inno setup e muitos outros e nada, pois nunca achei ninguém que desse a dica passo a passo na web e quando acha é perca de tempo.
Disculpa algo que me espressei errado, e desde já agradeço a atenção e que DEUS
Boa tarde André !
Em relação a chave estrangeira, tenho 3 chaves 1 para pedido 1 para item e 1 para produto, a do pedido funciona bem, o problema é que cada registro pode ter apenas item ou produto. Se uma das duas chaves estrangeiras ficar vazia não consigo puxar os registros em um DBGrid quando alterno de “Pedido” no DBGrid principal. Você conhece alguma solução ? Minha intenção é evita o máximo possível de código por isso não sei se conseguiria usar o Temporário. Abraços.
Grande, Jesse! Quanto tempo!
Rapaz, a sua dúvida parece ser um pouco mais complexa. Vou entrar em contato por e-mail para pedir mais detalhes, ok?
Abraço!
André, achei muito interessante o artigo e parabéns pelo mesmo. Até fiz um exemplo aqui e deu certo. Agora tenho uma aplicação que é distribuída gratuitamente. Por esse motivo usei o banco de dados firebird (fácil instalação) e tenho um problema quanto ao conteúdo do banco que não pode ser copiado por terceiros. Pensei em cryptografar os dados no banco firebird e quando o programa abrir jogar os dados numa tabela virtual e descrytografado para servir a pesquisa quando abrir o programa (ele só serve para pesquisa), só que fica lento o processo de copiar os dados para tabela virtual. Tem alguma sugestão de como faço para preservar meus dados no banco, só lembrando que a instalação do banco tem que ser fácil, porque muitas pessoas leigas instalarem esse programa.
Olá, Flavio, tudo certo?
Ainda não tive a oportunidade de trabalhar com dados criptografados da forma que você mencionou (descriptografar ao abrir o programa). O processo realmente pode ficar lento, já que é necessário percorrer todos os dados, descriptogrando-os e os atribuindo à uma tabela virtual. Porém, eu tenho outra sugestão: ao invés de utilizar criptografia, já considerou a possibilidade de alterar o usuário e senha do seu banco de dados? Isso significa que, mesmo que o arquivo seja copiado por terceiros, as informações não serão exibidas por questões de segurança.
Por outro lado, se você precisa da criptografia, eu recomendo que você altere o comportamento do programa para descriptografar os dados somente quando o usuário pesquisar por algum registro. Dessa forma, apenas o que foi pesquisado (poucos registros) serão descriptografados, e não uma tabela inteira, evitando a lentidão que você mencionou.
Espero ter ajudado!
Abraço!
André, como é uma aplicação que vai ser distribuída gratuitamente ela terá que ser de fácil instalação para usuários leigos. Eu ainda não achei um banco de dados de fácil instalação e que seja seguro por um banco de dados com senha. E para complicar mais um pouco eu trabalho com dois dbgrids na tela inicial, fornecendo pesquisa – é um programa que é uma agenda eletrônica, então em um grid pesquiso empresa por nome ou telefone, no outro pesquisa produtos dessas empresas. As empresas que compram a propaganda ficam em primeiro lugar e assim vai. O dbgrid fica conectado direto e mostra 13 empresas que fizeram propaganda. Se você tiver uma sugestão de banco de dados seguro com senha e fácil instalação eu ti agradeço a sugestão. Obrigado pela atenção.
Olá, novamente, Flavio!
Um banco de dados das características que você mencionou (leve, seguro e de fácil instalação) é o Firebird, no qual você já havia falado que utilizou. A instalação leva aproxidamente 30 segundos e a pasta ocupa pouco menos de 30MB no computador. Além disso, conforme citei no comentário anterior, é possível configurar uma senha para o banco de dados (arquivo FDB), de forma que, mesmo que outra pessoa com o Firebird instalado tenha acesso ao arquivo, não conseguirá ler as informações.
Há algum tempo publiquei um artigo sobre distribuição de aplicativos com Firebird. Confira:
https://www.andrecelestino.com/distribuindo-uma-aplicacao-com-firebird/
Muitos programadores também comentam sobre o SQLite, porém, ainda não tive a oportunidade de utilizá-lo em um projeto.
Abraço!
Ótimo, gostei realmente não sabida desta possibilidade, isso vai resolver um problema de compatibilidade com o XE7, uso bde como tmp.
Obrigado.
Oi Andre, boa tarde.
Parabens pelos artigos.
Não li todos os comentarios, assim não sei se ja tem algum que tiraria minha duvida. Se puder me ajudar, ficarei grata.
Uso uma aplicação cliente e outra servidora com soap. Ao abrir uma tabela vazia no cliente e esta possui master/detail (usando clientdataset) a aplicação demora a responder (o servidor e o banco de dados firebird estao numa máquina em outra cidade). Ha uma lentidão num simples open. Se a tabela nao for master/detail fica rapido (a sentença sql traz a tabela vazia where 1<1, por exemplo). Uso delphi xe5. Saberia me dizer como faço para resolver essa lentidao?
Obrigada
Olá, Magh, tudo bem?
Como há várias causas para essa lentidão, vou pedir mais detalhes por e-mail, ok?
Obrigado pelo comentário! Abraço!
Olá Andre, tudo bem?
Muito legal seu exemplo parabéns pela iniciativa.
Estou montando uma tela onde vou simular o desconto de cheques.
Numa tabela chamada CHEQUES, já tenho todos os cheques cadastrados.
Preciso agora achar uma forma de inserir os cheques que eu selecionei, no clientdataset, pois fazer a inserção de 1 por 1 dá muita mão de obra pro usuario.
Tem uma forma pratica de fazer isso?
Obrigado.
Olá, Renan.
Não consegui figurar o cenário da sua aplicação. Vou entrar em contato.
Abraço!
poderia dar um exemplo com RETURNING
Anotado, Wanderlei!
Vou elaborar um artigo sobre essa funcionalidade do Firebird com Delphi.
Abraço!
Olá Andre, tudo bem?
Notei que tem o campo Total na tabela de vendas e na tabela de itens, mas não achei a parte onde se grava o total na vendas, achei somente como gravar nos itens.
Como seria?
Olá, Renan, tudo bem?
Não abordei a gravação do valor total no artigo, mas esse valor pode ser calculado dentro do laço de repetição dos itens da venda, por exemplo:
Em seguida, basta atribuir o valor dessa variável ao DataSet de Vendas. 🙂
Abraço!
Como faria pra passar os dados de um DBGrid pra outro com 2 cliques?
Minha idéia seria a seguinte:
1 DBGrid com os resultados de uma busca SQL, dando 2 cliques na linha do resultado os dados dela vão para o outro DBGrid, onde vai armazenar todos os resultados clicados… como funciona em muitos PDVs, onde um grid é da tabela de produtos e o outro é a venda.
Olá, André, tudo bem?
Considerando que os dois componentes TDBGrid estão conectados a um DataSet, basta apenas copiar o registro atual de um DataSet para o outro, como no exemplo abaixo:
Aproveitando, sugiro também que você leia o artigo sobre Como copiar registros de um DataSet.
Abraço!
Boas Xará!
Testei com:
E rodou também, mas me deparei com um problema no DBGrid de destino:
Coloquei um CDS.Edit no OnKeyPress para alterar manualmente os dados desta forma:
Mas o lazarento só deixa digitar 1 caracter na célula QTD, ou seja, consigo digitar de 0 a 9, se quiser colocar 10 ele não aceita… fica só o zero.
Olá, André.
Eu precisaria analisar o projeto, mas, aparentemente, é um ajuste que deve ser feito no campo (Field) do DataSet. Acesso o Fields Editor do DataSet e verifique as propriedades do campo “QTD”. Provavelmente há algo relacionado com o tamanho 1.
Abraço!
Removi o CDS.Post; do final do evento de OnKeyPress e agora aceita qualquer quantidade de número…
O evento estava “fechando” a cada clique e “reabrindo” a edição” a cada tecla pressionada.
Agora fui obrigado a colocar um botão “Gravar” para dar o CDS.Post;
Bem observado, André. Esse fato não me passou pela cabeça.
A propósito, o evento
OnKeyPress
deve ser empregado com cautela, assim comoOnChange
eOnExit
.Abraço!
Boa tarde André…
Você não faz ideia do quanto seus artigos me ajudaram a “programar em Delphi na raça” e quase nenhuma ajuda senão a de artigos de pessoas com iniciativas como a sua. Meus sinceros parabéns e continue assim.
Mas, nem tudo é perfeito, rs… tenho uma terrível dúvida em trabalhar com tabelas temporárias. Estou usando TrxMemoryData e é a minha primeira aplicação usando componente dessa natureza. Minha dúvida é a seguinte: minha aplicação vai abrir a tabela real e popular a TbRxMemoryData. Com os dados o cliente ele vai alterar, incluir, excluir e assim por diante. Como faço para, após as mudanças na “Tabela Temporária”, o usuário atualizar com a “tabela real”? desde já obrigado mais uma vez.
Olá, DinhoEss, tudo jóia? Obrigado pelo comentário!
Você pode percorrer a tabela temporária e persistir os dados no banco. Veja o exemplo a seguir:
No entanto, você não sabe, de antemão, quais registros foram inseridos, alterados ou removidos, certo? Para isso, você terá que trabalhar com o Delta do RxMemoryData, que corresponde a uma tabela virtual que armazena todas as alterações que foram feitas no DataSet. Pelo Delta, você consegue identificar os registros que foram inseridos (para executar um Insert), atualizados (para executar um Update) ou removidos (para executar um Delete). Na verdade, é isso que o
ApplyUpdates
faz por trás das cortinas. 😉Caso eu não tenha respondido a sua dúvida, envie um e-mail para [email protected] com mais detalhes, ok?
Abração!
Olá, gostei dos tutoriais mas encontrei um problema e não estou entendendo. Vou gravando na hora de passar para o banco deu pau.
Exemplo: se gravo 5 registros ele sobrescreve todos pelo o último. Tem os 5 registros mas todos idênticos ao último.
Olá, Welton, tudo bem?
Provavelmente há um erro na semântica da gravação. Vou entrar em contato para solicitar o código da rotina, ok?
Abraço!
Saudações, André!!
Gostaria mais uma vez de parabenizá-lo pela sua disposição em compartilhar seu conhecimento com a comunidade.
Mais uma vez recorro a ti (“me ajuda aí”)… com uma questão. Atualmente estou desenvolvendo minhas aplicações com Lazarus IDE. Ainda não precisei utilizar em meus projetos o recurso de Tabela Temporária, mas recentemente comecei um projeto em que vou fazer uso do mesmo. Em meus testes utilizando o componente TrxMemoryData consigo fazer tudo que até então com TClientDataSet é possível, mas tem algo que está tirando meu sono: Ao usar um componente dbLookUpComboBox e mostrar os dados de uma tabela estrangeira, o mesmo não mostra os dados da mesma, apesar de eu ter notado que o componente dbLookUp foi preenchido mas os dados não ficam “Visíveis”.
Olá, Edvaldo! Boa noite!
Problemas com lookups geralmente estão relacionados com alguma falha na configuração. Como não conheço o Lazarus, não consigo informar mais detalhes, mas, mesmo assim, vou entrar em contato com você por e-mail, ok?
Abraço!
Bom dia André, tudo certo com você?
Estou com um problema, uso o componente kbmmemtable para manipulação de table temporárias, tenho um grid que recebe as informações, mas o usuário pode excluir diretamente do grid se quiser, então ele insere os dados que vai para a table (kbmmemtable) e em seguida é atualizado no grid, logo quando o cliente for clicar em salvar, vai da table para o banco direto.
Bom, o que ta acontecendo é o seguinte: existe um botão de excluir que exclui diretamente da table. Digamos que ele inseriu 3 registros, os 3 ainda estão na table, não foram para o banco de dados, logo quero excluir o segundo, então quando confirmo para ir para o banco de dados, ele exclui o 3º e não o segundo que eu queria. Ele sempre deleta o último registro que tem na tabela e preciso que delete um em especifico.
Se você souber como que faz isso, se possível usando o kbmmemtable, me avise.
Obrigado.
Olá, Fernando! Vou entrar em contato com você para pedir mais detalhes.
Boa Noite André,
Primeiramente gostaria de parabeniza-lo pelo magnifico trabalho em pro da Comunidade Delphiana. Seus artigos são de extrema importância para todos nós, sejamos amadores ou profissionais, iniciantes ou avançados.
Estou com um problema um tanto complicado e está justamente ligado ao uso do TClienteDataSet como tabela temporária.
Já há anos utilizando vários componentes nesse estilo tais como o TRXMemoryTable, depois o TKbmmemtable e ultimamente o TClientDataSet.
Faço uma varredura no banco de dados e vou armazenando valores nessa tabela. Acontece que em um determinado cliente, em que utilizam terminais como Thin Clients, o banco de dados é bastante grande, os registros vão “inflando” a memória até que gera um erro na aplicação, ainda que o servidor possua 32 gigas de memoria RAM. Já coloquei esse mesmo banco em meu PC e já utilizei essa mesma aplicação em outros clientes que não são Thin Clients e não acontece o erro.
Pergunto: Seria possível utilizar um recurso como tabela de memoria que possa de alguma forma utilizar talvez memoria em disco e efetuar esse processo sem que ocorra esse gargalo?
Desde já agradeço pela atenção.
Boa noite, Marley, tudo bem? Ótima pergunta!
Em primeiro lugar, agradeço imensamente pelo feedback! Pretendo continuar essa contribuição na comunidade por muito tempo.
Marley, por coincidência, eu tive esse mesmo problema há poucos meses. Em alguns momentos, o
TClientDataSet
recebia tantos registros que a aplicação subia para quase 1 GB na memória.Para resolver este problema, substitui o
TClientDataSet
peloTFDMemTable
do FireDAC. Este componente possui alguns mecanismos exclusivos para dados em memória, tornando-o mais eficiente que oTClientDataSet
.Faça esse teste!
Abraço!
Olá, preciso de fazer um select em uma tabela que contém nome, valor e cor.
Só que o programa que uso tenho que passar o nome dos Campos como label, value e com os.
Como faria sem mudar o meu banco de dados?
Olá, Victor.
Não entendi muito bem a sua dúvida. Você poderia enviar mais detalhes para [email protected]?
Abraço!
Olá!
Estou com o seguinte cenário… Tenho que ler arquivos XML e armazenar os dados (pode ser até mesmo em uma tabela temporária), em seguida terei que exibir esses dados em algum dbgrid ou stringgrid para o usuário conseguir visualizar as informações contidas no XML e somente após, confirmar a importação para o banco de dados…
Saberia me dizer alguma forma de realizar isso?
Agradeço desde já.
Bom dia, Guilherme!
Acredito que a parte mais custosa será ler o XML e importar o conteúdo para um DataSet. Não sei se você já tem conhecimento da leitura deste formato, mas, caso não tenha, nesse link do StackOverflow tem um exemplo.
Você terá que percorrer o conteúdo do XML, adicionando-o a um DataSet, como o
TClientDataSet
:Em seguida, basta ligar este DataSet a um componente
TDBGrid
, usando oTDataSource
como intermediário. Neste momento os dados já serão exibidos para o usuário.Para persistir no banco de dados, você pode percorrer o DataSet, incluindo linha por linha na tabela:
Opcionalmente, se você utilizar um DataSet que já estiver ligado ao banco de dados, fica ainda mais fácil, já que você precisa apenas aplicar as alterações:
Abraço!
Ola!
Primeiramente quero agradecer ao seu post, me auxilio muito, aprendi uma nova técnica, obrigado mesmo.
Porém estou enfrentando um cenário. Estou importando uma tabela mysql para ClientDataSet, ate ai beleza, consigo gravas os dados passando tudo bonitinho, porem ele não está fechando o processamento e acaba travando a tela, preciso salvar na memoria essa tabela pq vou recuperá-la pra fazer outro tipo de processo e depois volta para o mysql. Enfim, o meu banco tem em torno de 7k registro, se eu usar apenas o mysql funciona o que eu quero mas o mysql demora muito pra processar esse dados, fiz um teste de processo com o ClientDataSet e demorou 10min, e no Mysql de 3 a 4 horas, por isso quero usar o ClientDataSet, mas não consigo fecha o processo pq trava tudo.
Olá, Edson, bom dia!
Vou entrar em contato com você para entender melhor a sua dificuldade.
Abraço!
André, bom dia!
Gostaria de uma informação, por gentileza.
Estou criando um programa para controle simples de estoque, minhas dúvidas são as seguintes:
1 – Usando o comando de localização de registros em uma tabela real, como faria para carregá-los no grid da tabela virtual?
2 – Como procedo para dar baixa no estoque da tabela real, dos itens incluídos na tabela virtual?
Seria um procedimento de seleção dos itens da tbprodutos (Access .mdb) que quero retirar do estoque (incluidos no grid da tabela virtual), colocá-los nos campos de retida (Codbaixa, prodbaixa, quantbaixa) e o cálculo para baixar na tbprodutos. Espero que não tenha ficado muito conturbado.
Desde já, muito obrigado pelo apoio! Parabéns pela bela iniciativa.
Olá, André, bom dia!
Pelo que compreender, você terá que trabalhar com loops nos DataSets. Por exemplo, para copiar os registros selecionados da tabela real para a tabela virtual, basta percorrer a tabela real para copiar os registros:
Em seguida, para atualizar o estoque com base nos registros da tabela virtual, você pode percorrê-la e utilizar uma Query para executar um Update:
São só sugestões, ok, André?
Espero ter ajudado! Abraço!
Xará, muito obrigado pela presteza, respeito e gentileza na resposta. Foi de grande valia.
Faz tempo que não usava o Delphi, e apesar de várias pesquisas, não consegui encontrar essas informações.
Forte abraço e parabéns pela iniciativa!
Opa, Xará, fico feliz em ter ajudado.
Agradeço pelo retorno! Grande abraço e boa sorte!
André, bom noite!
Gostaria de uma informação, por gentileza.
Estou criando um programa para controle de ordem de serviço para uso pessoal:
Supondo que no memento de alterar minha ordem de serviço eu tenha 5 itens ja lançandos anteriormente como procedo para evitar quer estes itens não sejam duplicados no momento desta alteração ?
Desde já, muito obrigado pelo apoio! Parabéns pela bela iniciativa.
Olá, Hilton, bom dia!
Peço desculpas pela demora.
Há diferentes formas de fazer este controle. Vou listá-las aqui:
1) Utilizar uma variável ou sinalizador de alteração.
Essa solução é bastante comum. Geralmente utiliza-se uma variável para indicar se o registro está em estado de cadastro ou alteração. Se estiver em alteração, portanto, a inclusão dos itens é ignorada. Eu tenho o hábito de criar enumerados para esse tipo de finalidade, Hilton:
2) Utilizar as informações de UpdateStatus do registro.
Por fim, essa é uma solução mais técnica. Todos os registros de um DataSet carregam uma informação chamada
UpdateStatus
que indica o tipo de operação que foi realizado. Por exemplo,usInserted
indica que foi um registro inserido, eusModified
indica que foi um registro alterado. Você pode tirar proveito deste recurso para ignorar a inserção dos itens novamente.Obs: Essa informação é perdida após o
ApplyUpdates
. Utilize-a antes deste comando.3) Remover os itens e adicioná-los novamente.
Essa solução é mais “agressiva”, e consiste em adicionar novamente todos os itens quando há uma alteração.
Por exemplo, imagine que a Ordem Serviço possui 5 itens e você a abriu para alterar o cliente. Ao salvar essa alteração, você exclui os 5 itens e os adiciona novamente. Dessa forma, você não precisará de uma variável de controle (como na opção 1), e também minimiza o risco de duplicação:
Espero ter ajudado, Hilton.
Abraço!
Tentei alterar o field QTDE para float, pois preciso de valores “quebrados”, como peso, por exemplo, porém dá o erro
‘is not a format float’
Olá, André!
Você também deverá alterar a instrução que preenche esse campo no DataSet.
Substitua essa linha:
Por essa:
Vale lembrar que as casas decimais dos pesos podem ser digitadas com ponto (.) ou vírgula (,), portanto, se for o caso, você deverá adicionar também uma validação antes de preencher esse valor no DataSet.
Abraço!
Boa tarde.
Preciso utilizar o REPLACE do SQL Server para consultar buscando de um clientdataset, isso seria possível?
No filter mesmo … pois no caso não é utilizado um datasetprovider para fazer commandtext;
att;
Olá, Gabriel, boa noite!
A propriedade Filter do
TClientDataSet
não aceita funções específicas de bancos de dados.Você pode usar apenas expressões simples e wildcards (como “%” e “_”). Para mais detalhes, clique nesse link.
No seu caso, eu recomendaria criar uma herança de
TClientDataSet
e implementar um mecanismo personalizado de filtros, usando, por exemplo, Enumerators.Abraço!
Opa, gostaria de saber como faço para o código ser criado automaticamente, incrementando 1 ao código anterior. por exemplo, se o último código foi 12, quando o usuário criar um novo campo para inserção, ele já vir com o código 13? Obrigado
Olá, Fábio, boa noite!
Existem basicamente 3 formas de fazer este incremento.
A primeira é configurar a propriedade AutoGenerateValue do campo chave primária do DataSet para arAutoInc. Se você usa FireDAC, pode seguir este link.
A segunda opção é deixar o próprio banco de dados gerar a nova chave antes de gravar o registro na tabela. Isso pode ser feito através de Triggers (função que é disparada imediatamente antes da inserção, neste caso).
A terceira opção é criar uma tabela no banco de dados exclusivamente para armnazenar a última chave primária gerada. Ao inserir um novo registro, portanto, consulta-se essa tabela para obter o valor da última chave, incrementa o valor e o grava na tabela novamente. Isso garante que dois ou mais usuários não gere a mesma chave ao mesmo tempo. É uma forma de “reservar” a chave primária. No entanto, essa última opção é geralmente usada em aplicações multiusuário.
Espero ter ajudado.
Abraço!
Olá André tudo bom ? André e para alterar a estrutura da tabela temporária em RUN-TIME tipo alterar o tamanho de um determinado campo da tabela?
Olá, Vilmar, tudo bem?
Nesse caso, você precisa fechar o DataSet (Close) antes de alterar a estrutura. Não é possível alterá-la enquanto o DataSet estiver aberto.
Em seguida, utilize o FieldDefs para adicionar ou remover Fields. Quando finalizar a alteração, basta chamar o CreateDataSet novamente.
Espero ter ajudado. Abraço!
Saudações André, mais uma vez, parabéns por esse post – São pessoas como você que ajuda na “construção do aprendizado” de muitos, inclusive o meu. Estou com um “pequeno problema” acho que pode me ajudar. Estou usando por opção – e para minhas aplicações ele me serve de montão que é o dataset rxMemoryData. Já faz algum tempo que utilizo os recursos desse componente que faz tudo “direitinho” – até agora. Em minha aplicação eu preciso serializar os dados e passar para dentro de uma classe em Json, até aí tudo funciona – ou melhor quase tudo. Dentro dessa classe eu preciso LER os dados que se encontra no componente e isso está perfeito (first, next, Locate, etc..). Mas o DANADO do POST não está “persistindo” os dados quando eu preciso Registrar algo no conjunto de dados do rxMemoryData. Ele aceita Edit, aceita atribuir o valor independente do tipo (integer, String), mas na hora de POSTAR… ele não grava os dados. Por favor, será que pode me ajudar nessa empreitada?! só pra deixar registrado, após o Create eu já ATIVO e ao verificar se existe dados, sempre está lá os dados SERIALIZADOS. Forte abraço e Obrigado desde já.
Olá, Edvaldo.
Primeiramente, obrigado pelo feedback!
Vou entrar em contato com você sobre esse comentário.
Abraço!
André boa tarde e muito obrigado pelo post!
if not VarIsNull(cdsTemporario.Aggregates[0].Value) then
begin
Total := FormatFloat('###,###,##0.00', cdsTemporario.Aggregates[0].Value);
StatusBar.Panels[0].Text := 'Total: ' + Total;
StatusBar.Panels[1].Text := 'Qtde Registros: ' + IntToStr(cdsTemporario.RecordCount);
end;
Como você pegou o valor do campo “cdsTemporario.Aggregates[0].Value”?
Isso pra mim é novo 🙂
Opa, boa noite, Paulo!
Aggregates é um recurso para agregar valores no DataSet, como cálculos matemáticos.
É necessário criar os Aggregates antes de utilitá-los. No exemplo que disponbilizei, o Aggregate foi criado através da propriedade “Aggregates” do componente “cdsTemporario”. Observe que inseri a expressão “sum(TOTAL)”. Isso significa que, para cada registro inserido, a soma do campo “Total” de todos os registros será armazenado nesse Aggregate.
Alternativamente, você pode obter o valor do Aggregate dessa forma:
cdsTemporario.Aggregates.Find('TOTAL').Value
Abraço!