Delphi Tabela Temporária
Delphi Tabela Temporária
Delphi Tabela Temporária
ClientDataSet Conceito
Postado por Andr Luis Celestino Publicado em Artigos, Delphi, Programao
Uma das vantagens de um frum de programao
observar as dvidas mais frequentes dos usurios e tentar ajud-los de uma forma mais
prtica. Dessa vez, notei que muitos desenvolvedores tm dificuldades em compreender,
criar e manipular tabelas temporrias no Delphi utilizando ClientDataSet. Alm de ser
um recurso muito til, trabalhar com tabelas temporrias no exige conhecimentos
avanados de programao.
Preparado pra mais um pequeno tutorial sobre desenvolvimento?
Tabela temporria? O que isso?
Tambm conhecida como tabela virtual, uma tabela temporria capaz de armazenar
registros em memria sem a necessidade de estar conectada a um banco dados,
diferentes das tabelas fsicas, que armazenam os dados em disco.
Os registros da tabela temporria so apagados automaticamente quando a aplicao
encerrada ou quando se utiliza um comando prprio pra isso, no qual veremos no
prximo artigo. Um grande recurso da tabela temporria permitir manipular estes
dados em memria, como incluses, alteraes, excluses e filtros, tal como podemos
fazer com tabelas fsicas no banco de dados.
Mas qual a vantagem de utilizar uma tabela temporria no meu projeto?
Bom, depende do tipo de projeto que voc est desenvolvendo. Tabelas temporrias, em
linhas gerais, so teis para trabalhar com dados locais na aplicao de forma dinmica.
Por exemplo, em um ambiente master/detail, uma tabela temporria pode armazenar os
registros filhos antes mesmo do registro mestre ser gravado no banco de dados.
Ainda no entendi. Poderia dar um exemplo?
Claro que sim.
Imagine que estamos desenvolvendo um sistema para controle de vendas.
No nosso banco de dados, teremos as seguintes tabelas relacionadas venda:
-- Tabela VENDAS
COD_VENDA
DATA
COD_CLIENTE
TOTAL
-- Tabela ITENS
COD_VENDA (chave estrangeira referenciando a tabela VENDAS)
COD_PRODUTO
QTDE
VALOR
TOTAL
Agora, suponha que iremos gravar uma nova venda no sistema com os seguintes dados:
COD_VENDA = 1
DATA = 15/02/2013
COD_CLIENTE = 20
TOTAL = ?
U, cad o total?
Voc deve concordar que, para informar o valor total, necessrio
adicionar os itens, ou seja, precisamos som-los para calcular o valor total da venda, no
?
Por outro lado, para adicionar os itens precisamos do cdigo da venda, j que as duas
tabelas so relacionadas por uma chave estrangeira:
(ITENS.COD_VENDA = VENDAS.COD_VENDA).
Agora raciocine comigo: se tentarmos adicionar um item sem o cdigo da venda ou com
o cdigo de uma venda que (ainda) no existe, receberemos o erro de violao de
chave estrangeira, visto que, na verdade, tentaramos adicionar um registro filho sem a
existncia do registro pai.
Pois bem, e para obtermos o cdigo da venda, precisamos primeiro grav-la no banco de
dados, certo? Mas espere a conforme vimos acima, no temos o valor total pra gravar
a venda!
E ento entramos em um impasse:
- No podemos gravar primeiro a venda, pois no temos os itens
- No podemos gravar primeiro os itens, pois no temos a venda
E agora? J sei! Vamos gravar a venda sem o valor total!
Muitos desenvolvedores utilizam essa tcnica, se que podemos cham-la assim.
Aplicando nossa realidade, gravamos a venda sem o valor total, apenas para obter o
cdigo da venda. Dessa forma, podemos gravar os itens da venda normalmente, e aps
grav-los, calculamos o valor total e alteramos o registro da venda, atualizando o total.
Em um contexto procedimental, ficaria dessa forma:
Gravar a venda sem o valor total;
Obter o cdigo da venda gravada;
Gravar os itens utilizando o cdigo da venda obtido;
Somar os itens para calcular o total;
Editar a venda e atualizar o valor total.
Observe que para cada venda ser necessrio fazer duas operaes
na tabela de vendas: uma de insero e outra para alterao. Eu, particularmente, no
sou de acordo com esse procedimento.
Em um exemplo, imagine que gravamos a venda, mas ao comear a adicionar os itens, a
energia cortada ou o usurio decide fechar a tela por algum motivo. Obviamente o
registro ficar incompleto, j que teremos uma venda sem itens gravada no banco de
dados. Tecnicamente, ser um registro mestre sem registros filhos.
No caso da energia acabar, o problema pode ainda se agravar um pouco mais. Ao
reiniciar o sistema, o usurio ir gerar uma nova venda, visto que, aos olhos do usurio,
a outra venda no foi gravada. No fim da histria haver 2 vendas idnticas no sistema:
uma sem itens (perdida) e outra vlida.
Mas neste caso s excluir essa venda incompleta, no ?
Sim, pode ser. Mas para isso, voc ter que implementar um controle eficiente no seu
sistema para identificar esses registros problemticos. E tambm, lembre-se que cada
vez que isso ocorrer, o cdigo da venda ser perdido (pulado) no banco de dados.
A prepare-se para receber algumas ligaes do usurio perguntando:
Por qu o sistema pulou o nmero da venda?
A venda n X sumiu do sistema
Existe uma venda aqui sem itens!
Certo, e qual a sugesto para resolver isso?
Opa, chegou aonde eu queria!
Amigos, todos os problemas citados acima podem ser resolvidos utilizando tabelas
temporrias! Durante a incluso da venda, ao invs de gravarmos os itens no banco de
dados, iremos grav-los em uma tabela temporria. Se a energia acabar, nada ser
afetado, j que nenhuma alterao foi feita no banco de dados.
Este ser o nosso procedimento:
Inserir os itens na tabela temporria;
Ao gravar a venda, os itens da tabela temporria sero copiados para a tabela
fsica, tudo em um mesmo mtodo.
Desta maneira, iremos reduzir bastante a possibilidade de uma venda ficar incompleta,
visto que a gravao da venda e os itens da venda acontecero praticamente ao mesmo
tempo. E o melhor: durante a incluso da venda no faremos nenhuma operao no
banco de dados, somente na gravao.
Legal! Como fao pra criar uma tabela temporria?
Pessoal, como essa parte conceitual de tabelas temporrias ficou um pouco extensa, vou
fazer um suspense e deixar a parte prtica para o prximo artigo, ok?
[Delphi] Tabela temporria com
ClientDataSet Prtica
Postado por Andr Luis Celestino Publicado em Artigos, Delphi, Programao
Ol, leitores! Esse artigo a continuao do tema sobre tabelas
temporrias com ClientDataSet no Delphi. No artigo anterior, apresentei o conceito,
vantagens e um exemplo de cenrio no qual uma tabela temporria pode ser utilizada
para evitar inconsistncias. Aps a teoria, finalmente vamos partir para a prtica!
Criaremos uma tabela temporria utilizando o mesmo exemplo de cenrio mencionado
no primeiro artigo!
Recapitulando o artigo anterior, lembre-se que temos a seguinte tabela de itens da
venda:
-- Tabela ITENS
COD_VENDA (chave estrangeira referenciando a tabela VENDAS)
COD_PRODUTO
QTDE
VALOR
TOTAL
Nosso objetivo ser criar uma tabela temporria para armazenar os itens da venda em
memria, e somente quando o usurio clicar pra gravar a venda que estes itens sero,
de fato, inseridos no banco de dados.
Para criar uma tabela temporria, vamos utilizar o componente TClientDataSet,
disponvel na paleta Data Access do Delphi. Adicione-o no formulrio, 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 temporria.
Para adicionar um novo campo, clique com o boto direito dentro dessa janelinha e
selecione a opo New Field.
Uma nova janela ser aberta para preencher as propriedades do campo que ser
adicionado.
No nosso caso, adicionaremos 4 campos:
Cdigo 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 no adicionamos a descrio do produto?
Caro leitor, se voc j trabalhou com master/detail alguma vez ou tem noes de
normalizao de dados, sabe que no devemos gravar a descrio do produto na tabela
de itens da venda, j que temos o cdigo do produto como chave estrangeira. Se
adicionarmos a descrio do produto, teremos valores duplicados no banco de dados (na
tabela Produtos e na tabela Itens da Venda), e isso desnecessrio. Apenas com o
cdigo do produto podemos facilmente buscar a descrio atravs de um
relacionamento entre tabelas (inner join).
Quando terminar de adicionar os itens, feche o Fields Editor. Em seguida, clique com o
boto direito no cdsTemporario e selecione a opo Create DataSet.
Pronto! A partir de agora j temos uma tabela temporria criada! A seguir, veja que a
manipulao de dados tambm bem simples.
Para inserir um registro na tabela temporria, utilizaremos o mtodo Append para
coloc-la em modo de insero e o mtodo Post para gravar o registro, tal como se
estivssemos trabalhando com uma tabela fsica. Considerando que temos componentes
TEdit para cada campo da tabela temporria, o cdigo de insero ficaria da seguinte
forma:
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 no h segredo algum! Para visualizar as inseres, adicione um
componente TDBGrid e um TDataSource no formulrio e faa as ligaes entre os
trs componentes para exibir os dados.
Caso seja necessrio alterar um registro da tabela temporria, basta utilizar o mtodo
Edit para colocar a tabela em modo de edio (alterao), ao invs do Append. Ah, e
lembre-se de utilizar o Post para gravar as alteraes. Por fim, se for necessrio deletar
um registro da tabela temporria, utilize o mtodo Delete:
cdsTemporario.Delete;
Ok! Agora sei criar uma tabela temporria e manipular os dados, mas como devo
proceder para gravar estes registros no banco de dados?
Muito simples! Mas antes disso, precisamos obter o cdigo da venda para que seja
possvel 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 cdigo da venda for um campo
autoincremento, ele pode ser obtido atravs de uma funo de retorno (como o
RETURNING do Firebird ou @@IDENTITY do SQL Server). Por outro lado, se o
cdigo da venda preenchido pelo sistema, podemos utilizar a funo MAX em uma
SQL, que retorna o valor mximo de um campo em uma tabela. No nosso caso, seria o
campo Cdigo da venda recm inserida:
var
CodigoVenda: integer; // varivel para armazenar o cdigo da venda
begin
// limpa a instruo SQL
Query1.SQL.Clear;
// adiciona uma instruo SQL com a funo MAX
Query1.SQL.Add('Select MAX(COD_VENDA) as CodigoVenda from VENDAS');
// abre a Query
Query1.Open;
// obtm o cdigo 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 fsica de itens da venda no banco de dados. Para copiar os dados
da tabela temporria para a tabela fsica, basta realizar um lao de repetio (loop) e
gravar os itens, um a um. Complementando o cdigo-fonte acima, nossa sintaxe
finalmente ficar da forma abaixo. Para facilitar a visualizao, quebrei algumas linhas
do cdigo, ok?
var
CodigoVenda: integer;
begin
{ aqui estaria o cdigo de gravao 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
temporria
while not (cdsTemporario.EOF) do // lao de repetio
begin
cdsItensVenda.Append; // coloca a tabela fsica em modo de
insero
// copia os valores da tabela temporria para a tabela fsica
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').AsInteger :=
cdsTemporario.FieldByName('VALOR').AsInteger;
cdsItensVenda.FieldByName('TOTAL').AsInteger :=
cdsTemporario.FieldByName('TOTAL').AsInteger;
cdsItensVenda.Post; // grava o item da venda
cdsItensVenda.ApplyUpdates(0); // persiste a gravao no banco de
dados
// deleta o registro da tabela temporria
// isso far com que o prximo registro seja lido
cdsTemporario.Delete;
end;
end;
Em poucas linhas gravamos a venda e todos os itens dela em um mesmo mtodo!
bem mais rpido e seguro, no ?
Clique aqui e baixe um exemplo de tabela temporria com ClientDataSet
desenvolvido em Delphi 7. Neste exemplo, procurei incrementar mais algumas
funes (como o recurso de Aggregates), e adicionei um campo Descrio para
aprimorar a compreenso de tipos de dados no ClientDataSet. Analise o cdigo e teste o
exemplo. Caso houver dvidas, no hesite em entrar em contato!
Antes que eu me esquea, tabelas temporrias no servem exclusivamente para o
cenrio apresentado acima. Voc pode utilizar tabelas temporrias para vrias
finalidades em diversas situaes. O importante compreender o conceito e saber
aplic-lo quando necessrio.
Por hoje s, leitores! Grande abrao!
[Delphi] Tabela temporria com
ClientDataSet Final
Postado por Andr Luis Celestino Publicado em Artigos, Delphi, Programao
Bom, hora de fechar o tema sobre tabelas temporrias no Delphi! Espero
que nos dois primeiros artigos voc tenha compreendido, de forma satisfatria, como
trabalhar com tabelas temporrias utilizando ClientDataSet. Este ltimo artigo apenas
apresenta algumas observaes relacionadas a tabelas temporrias que podem ser teis
durante o desenvolvimento.
Tabelas temporrias tambm podem ser criadas em tempo de execuo, bem como a
definio dos seus campos. Observe o exemplo abaixo, onde instancio um
TClientDataSet e adiciono trs 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 tambm! Caso voc no conhea, campos agregados servem para
realizar clculos 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 necessrio esvaziar a tabela temporria, como por exemplo, no
boto Limpar ou Cancelar de um formulrio. Essa instruo pode ser realizada com
apenas uma linha de cdigo:
ClientDataSet1.EmptyDataSet;
Barbada, no?
Alm disso, tabelas temporrias tambm permitem a navegao entre os registros na
memria utilizando os mtodos 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 prximo registro
Outro recurso bastante interessante do ClientDataSet o clone do conjunto de dados.
Atravs do comando CloneCursor possvel copiar os dados de um ClientDataSet para
outro, e ento manipul-los de maneira independente.
// ClientDataSet2 "clona" os dados de ClientDataSet1
ClientDataSet2.CloneCursor(ClientDataSet1, True);
Agora, imagine que estamos utilizando uma tabela temporria para gravar itens de uma
venda. No interessante que produtos repetidos sejam inseridos na tabela, concorda?
Afinal, se o cdigo do produto fizer parte da chave primria, ocorrer um erro ao gravar
os itens da venda.
Para resolver isso, podemos utilizar a funo Locate e controlar a insero 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, no h segredo. No exemplo abaixo, tenho um componente do
tipo TEdit chamado edtPesquisa, e permito o filtro da descrio de um registro na
tabela temporria conforme o contedo digitado no campo:
ClientDataSet1.Filter := 'DESCRICAO like ' +
QuotedStr(edtPesquisa.Text + '%');
ClientDataSet1.Filtered := True; // ativa o filtro
S lembrando que o smbolo de porcentagem permite que todos os registros que iniciem
com a palavra digitada em edtPesquisa sejam encontrados. Por exemplo, se o usurio
digitar a letra A, todas as descries que comeam com essa letra sero filtradas.
Mas ateno: no esquea de desativar o filtro quando for necessrio trabalhar com
todos os registros da tabela temporria.
Bom, pessoal, espero que tenham gostado dessa srie de artigos sobre tabelas
temporrias, e que de alguma forma venha a ser til pra vocs! Boa sorte no trabalho e
at breve!
Confira os outros artigos:
Tabela temporria com ClientDataSet Conceito
Tabela temporria com ClientDataSet Prtica
Tabela temporria com ClientDataSet Final