USANDO GENERATORS
Helio Souza
Respostas
Marco Salles
02/07/2011
Function TDm.RetornaChaveFirebird(Indice: String): Integer;
var
{Declarando a query que sera utilizada para pegar o valor gerado.}
QryId: TSQLQuery;
begin
{Instanciado o objeto.}
QryId := TSQLQuery.Create(nil);
try
{Preparando a query para a execução}
QryId.SQLConnection := Conexao;
{SQL que irá pegar o registro gerado, dado onome do generator da tabela.}
QryId.SQL.Add('SELECT GEN_ID(' + Indice +',1) AS ID_RET FROM RDB$DATABASE');
QryId.Open;
if QryId.IsEmpty then
{Não ocorreu geração, por isso o cadastro não pode continuar.}
raise Exception.Create('Não foi criado nenhum valor.')
else
{Retornando o valor gerado.}
result := QryId.FieldByName('ID_RET').AsInteger;
finally
{Apos o uso da query, liberando a memoria.}
QryId.Free;
end;
end;
para pegar o valor do genator vc faz assim
{Pegando o valor incrementado no banco}
CdsPadrao.FieldByName('PRO_CODIGO').AsInteger := Dm.RetornaChaveFirebird('gen_produto_id');
como voce vai pegar , quando vc vai pegar vai depender de sua lógica ,
Agora eu particularmente pego esse valores pelo DataSetProvider no evento BeforeUpdateREcord...
Tem um post do diego macário
https://www.devmedia.com.br/forum/delphi/395811-Master-Detail-com-incremento-negativo.html
é um pouco complicado para quem esta inicializando .. Mas no meu ponto de vista é a maneira mais limpa de gerar
esses codigos
Mas vc vai tentando com esta funão padrão acima e a medida que for dando erro , vai postando para chegar num mesmo MDC
Emerson Nascimento
02/07/2011
eu estava dando uma olhada no tópico que você sugeriu para esclarecer a dúvida do colega e observer um detelhe curioso: você se preocupou em guardar o valor do ID ta tabela principal numa variável pra depois atribuí-lo à tabela filha. isso não é necessário. quando o DatasetProvider executa o BeforeUpdateRecord, ele o faz para cada Dataset do Provider, partindo sempre do Pai.
Então, quando o BeforeUpdateRecord estiver atualizando uma tabela filha, ele já passou pela tabela pai e atribuiui o valor ao campo ID da tabela pai. Com isso basta obter esse valor diretamente do Dataset pai, sem qualquer variável.
Usando como exemplo do tópico que você sugeriu, fica assim:
if (UpdateKind = ukInsert) then
begin
if (SourceDS = sqlfornecedor) then
// begin
// eu não faria essa verificação, pois SE ESTÁ INSERINDO
// é porque é necessário atribuir o ID, então eu tiraria
// o if e atribuiria direto.
// if (DeltaDS.FieldByName('CHAVE_FORNECEDOR').Value < 0) then
// begin
// Id := GenID('FORNECEDOR');
DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue := GenID('FORNECEDOR')
// end;
// end
else
if (SourceDS = sqlcontato) then
begin
DeltaDS.FieldByName('CHAVE_CONTATO').NewValue := GenID('CONTATO');
// aqui atribui o ID da tabela pai, sem necessidade de qualquer análise
DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue := //AQUI É NEWVALUE
DeltaDS.DatasetField.Dataset.FieldByName('CHAVE_FORNECEDOR').Value; //AQUI É VALUE
end;
end;
note que na atribuição da tabela filha, é usado DeltaDS.DatasetField.Dataset, que é para alcançar o Dataset princial.
eu estava escrevendo um artigo - faz alguns anos - sobre como trabalhar com NestedDatasets, usando vários recursos, como atribuição mestre/detalhe, geração de logs (auditoria), etc, mas ainda não consegui terminar...
Marco Salles
02/07/2011
Não .. O Valor atribuido não vem de uma tabela , mas sim de uma constante Tipada .São Valores negativos , decrementados e não repetidos para evitar duplicidade . Mas isto não teria importância . O importante é entender que são os PRIMARY_KEYS da tabela Mastre e DEtalhe que são substituidas antes do provider resolver o Sql
Veja eu não costumo a cada post em um Cds dar ApplayUpdate . Logo o BeforeUpdateRevcor não sera executado
Porém a cadasPost eu ( O Diego_Macário) necessitava de valores para Chaves (Porque ele Reclama se esses valores não forem atribuidos ).. Não ha chave do Relaciomanento que automaticamente é atribuida para a filha, mas sim os Primary_Key .. A idéia era trabalhar desconectado , inserir editar alterar e no fim dar o ApplayUpdates .. Ai sim seria Chamado o evento BeforeUpdateRecord que teria a função de Substituir os Valores Atribuidos ao Primary_Key , porque esses valores eram negativos e decrementados (-1,-2,-3...-10....etc) Para evitar a mensagem de Duplicidade . Por isto ha necessidade dos Ifs que vc vé no código
Poderia a cada post , dar um ApplayUpdates , Poderia a cada Post atribuir um Valor para os Primary_Key utilizando a função GENID('') ETC.. Mas na época optou por trabalhar desconectado
Confesso que eu gostei do Modelo , e eu não sei se soube explicar ou se eu lhe entendi. mas a idéia não era
dar o ApplayUpdates chamando assim o BeforeUpdateRecord ,, E sim desconectadamente
Helio Souza
02/07/2011
procedure Tdmvendas.dspitemprecoclienteAfterUpdateRecord(Sender: TObject;
SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
UpdateKind: TUpdateKind);
begin
if (UpdateKind = ukInsert) then
begin
if (SourceDS = sqlClientes) then //TABELA CLIENTE TABELA PAI
// begin
// eu não faria essa verificação, pois SE ESTÁ INSERINDO
// é porque é necessário atribuir o ID, então eu tiraria
// o if e atribuiria direto.
// if (DeltaDS.FieldByName('CHAVE_FORNECEDOR').Value < 0) then
// begin
// Id := GenID('FORNECEDOR');
DeltaDS.FieldByName('CLI_CODIGO').NewValue := ('GEN_CADCLIENTECODIGO_ID') //O CAMPO CLI_CODIGO É ONDE É GERADO O CODIGO DO CLIENTE PELO GENERATOR
// end;
// end
else
if (SourceDS = sqlitemprecocliente) then //TABELA ONDE VAR O PREÇO DO PRODUTO POR CIENTE TABELA FILHO
begin
DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue := ('GEN_CADITEMPRECOCLIENTE_ID'); //O CAMPO ITEMPRE_CODCLIENTE É ONDE VAI RECEBER O CODIGO DO CLIENTE GERADO PELO GENERATOR
// aqui atribui o ID da tabela pai, sem necessidade de qualquer análise //O GEN_CADITEMPRECOCLIENTE_ID ELE É UM GENERATOR SÓ PARA RECEBER O CAMPO GEN_CADCLIENTECODIGO_ID
DeltaDS.FieldByName('CLI_CODIGO').NewValue := //AQUI É NEWVALUE //FOI CRIADO NORMALMENTE IGUAL AOS DEMAIS ESTA CERTO COMO FOI CRIADO
DeltaDS.DatasetField.Dataset.FieldByName('CLI_CODIGO').Value; //AQUI É VALUE
end;
end;
end;eu estava dando uma olhada no tópico que você sugeriu para esclarecer a dúvida do colega e observer um detelhe curioso: você se preocupou em guardar o valor do ID ta tabela principal numa variável pra depois atribuí-lo à tabela filha. isso não é necessário. quando o DatasetProvider executa o BeforeUpdateRecord, ele o faz para cada Dataset do Provider, partindo sempre do Pai.
Então, quando o BeforeUpdateRecord estiver atualizando uma tabela filha, ele já passou pela tabela pai e atribuiui o valor ao campo ID da tabela pai. Com isso basta obter esse valor diretamente do Dataset pai, sem qualquer variável.
Usando como exemplo do tópico que você sugeriu, fica assim:
if (UpdateKind = ukInsert) then
begin
if (SourceDS = sqlfornecedor) then
// begin
// eu não faria essa verificação, pois SE ESTÁ INSERINDO
// é porque é necessário atribuir o ID, então eu tiraria
// o if e atribuiria direto.
// if (DeltaDS.FieldByName('CHAVE_FORNECEDOR').Value < 0) then
// begin
// Id := GenID('FORNECEDOR');
DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue := GenID('FORNECEDOR')
// end;
// end
else
if (SourceDS = sqlcontato) then
begin
DeltaDS.FieldByName('CHAVE_CONTATO').NewValue := GenID('CONTATO');
// aqui atribui o ID da tabela pai, sem necessidade de qualquer análise
DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue := //AQUI É NEWVALUE
DeltaDS.DatasetField.Dataset.FieldByName('CHAVE_FORNECEDOR').Value; //AQUI É VALUE
end;
end;
note que na atribuição da tabela filha, é usado DeltaDS.DatasetField.Dataset, que é para alcançar o Dataset princial.
eu estava escrevendo um artigo - faz alguns anos - sobre como trabalhar com NestedDatasets, usando vários recursos, como atribuição mestre/detalhe, geração de logs (auditoria), etc, mas ainda não consegui terminar...
Marco Salles
02/07/2011
Helio Souza
02/07/2011
Emerson Nascimento
02/07/2011
Não .. O Valor atribuido não vem de uma tabela , mas sim de uma constante Tipada .São Valores negativos , decrementados e não repetidos para evitar duplicidade . Mas isto não teria importância . O importante é entender que são os PRIMARY_KEYS da tabela Mastre e DEtalhe que são substituidas antes do provider resolver o Sql
Veja eu não costumo a cada post em um Cds dar ApplayUpdate . Logo o BeforeUpdateRevcor não sera executado
Porém a cadasPost eu ( O Diego_Macário) necessitava de valores para Chaves (Porque ele Reclama se esses valores não forem atribuidos ).. Não ha chave do Relaciomanento que automaticamente é atribuida para a filha, mas sim os Primary_Key .. A idéia era trabalhar desconectado , inserir editar alterar e no fim dar o ApplayUpdates .. Ai sim seria Chamado o evento BeforeUpdateRecord que teria a função de Substituir os Valores Atribuidos ao Primary_Key , porque esses valores eram negativos e decrementados (-1,-2,-3...-10....etc) Para evitar a mensagem de Duplicidade . Por isto ha necessidade dos Ifs que vc vé no código
Poderia a cada post , dar um ApplayUpdates , Poderia a cada Post atribuir um Valor para os Primary_Key utilizando a função GENID('') ETC.. Mas na época optou por trabalhar desconectado
Confesso que eu gostei do Modelo , e eu não sei se soube explicar ou se eu lhe entendi. mas a idéia não era
dar o ApplayUpdates chamando assim o BeforeUpdateRecord ,, E sim desconectadamente
Marcos, a forma que eu sugeri funciona da forma que você exemplificou:
Preciso digitar um pedido com 30 itens. Dou o Post a cada digitação de item de o ApplyUpdates somente no final de toda a digitação (posso até trabalhar desconectado nesse período).
Quando o ApplyUpdates é executado, o DatasetProvider tentar gravar efetivamente o registro da tabela principal, depois cada registro de cada tabela filha individualmente, e com isso basta pegar o ID já gravado na tabela pai, da forma que eu exemplifiquei, porque com certeza ele já está atualizado.
Por isso não há necessidade de obter nenhum ID enquanto digita-se o documento e nem gravar o ID em qualquer variável (pelo menos foi assim que eu entendi o seu código: você grava o ID do pai numa variável e depois replica para os filhos).
agora tentando ajudar nosso colega: isso tudo que está sendo discutido aqui, bem como o exemplo que eu passei, só funcionam em NestedDatasets e no evento BEFOREUpdateRecord (ANTES DE GRAVAR). No AFTER não vai resolver.
Emerson Nascimento
02/07/2011
SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
UpdateKind: TUpdateKind);
begin
if (UpdateKind = ukInsert) then
begin
if (SourceDS = sqlClientes) then //TABELA CLIENTE TABELA PAI
DeltaDS.FieldByName('CLI_CODIGO').NewValue := ('GEN_CADCLIENTECODIGO_ID') //O CAMPO CLI_CODIGO É ONDE É GERADO O CODIGO DO CLIENTE PELO GENERATOR
else
if (SourceDS = sqlitemprecocliente) then //TABELA ONDE VAR O PREÇO DO PRODUTO POR CIENTE TABELA FILHO
begin
DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue := ('GEN_CADITEMPRECOCLIENTE_ID'); //O CAMPO ITEMPRE_CODCLIENTE É ONDE VAI RECEBER O CODIGO DO CLIENTE GERADO PELO GENERATOR
// aqui atribui o ID da tabela pai, sem necessidade de qualquer análise //O GEN_CADITEMPRECOCLIENTE_ID ELE É UM GENERATOR SÓ PARA RECEBER O CAMPO GEN_CADCLIENTECODIGO_ID
DeltaDS.FieldByName('CLI_CODIGO').NewValue := //AQUI É NEWVALUE //FOI CRIADO NORMALMENTE IGUAL AOS DEMAIS ESTA CERTO COMO FOI CRIADO
DeltaDS.DatasetField.Dataset.FieldByName('CLI_CODIGO').Value; //AQUI É VALUE
end;
end;
end;
não entendi:
qual é o campo ID da tabela filha?
qual é o campo que relaciona a tabela filha com a tabela pai ?
Marco Salles
02/07/2011
Preciso digitar um pedido com 30 itens. Dou o Post a cada digitação de item de o ApplyUpdates somente no final de toda a digitação (posso até trabalhar desconectado nesse período).
Quando o ApplyUpdates é executado, o DatasetProvider tentar gravar efetivamente o registro da tabela principal, depois cada registro de cada tabela filha individualmente, e com isso basta pegar o ID já gravado na tabela pai, da forma que eu exemplifiquei, porque com certeza ele já está atualizado.
Por isso não há necessidade de obter nenhum ID enquanto digita-se o documento e nem gravar o ID em qualquer variável (pelo menos foi assim que eu entendi o seu código: você grava o ID do pai numa variável e depois replica para os filhos).
Vc ainda não entendeu o Modelo emerson
Vaje:
Preciso digitar um pedido com 30 itens. Dou o Post a cada digitação de item
Neste momento na tabela Filha a cada Post é necessário GERAR UM ID .( Primary Key ) ...
Como Fazer ??? Qual O ID atribuir ????
Não vou fazer um Select para Retornar O ID porque estou trabalhando desconectado ...
No Modelolo é atribuido o valor negativo para este item
Mestre
Item_Venda :=-1 // ******** Eu preciso deste Valor para o Relacionamento ***********
Detalhe .............. Relacionamento ... Item_Venda:=-1
Item_Pedidos:=-1;
Item_Pedidos=:-2;
Item_Pedidos=:-30;
Mestre
Item_Venda:=-2; // ******** Eu preciso deste Valor para o Relacionamento ***********
Detalhe .............. Relacionamento ... Item_Venda:=-2
Item_Pedidos:=-31;
Item_Pedidos=:-32;
...
Item_Pedidos=:-60;
No fim de tudo no no BeforeUpdate Modificamos e atribuimos resolvendo tudo no Provider
Item_Venda :=1
Item_Pedidos:=1;
Item_Pedidos=:2;
Item_Pedidos=:30;
Item_Venda:=2;
Item_Pedidos:=31;
Item_Pedidos=:32;
...
Item_Pedidos=:60;
Continuando Desconectado ... Proxima Venda
Mestre
tem_Venda :=-1
Item_Pedidos:=-1;
Item_Pedidos=:-2;
Item_Pedidos=:-30;
e assim segue O Modelo .... Qualquer coisa se ainda tiver algum modelo simples que simplifique a situação apresentada acima , favor me envie a tabela e o projeto para analisar e ficarei grato se tiver alguma novidade a cerca do apresentado acima . Caso confirme esta posição eu publicamente retrocederei minha opinião a cerca do assunto.
Você sabe que isto não é de forma nenhuma pessoal a vc , talves não saiba o quanto lhe admiro e lhe prezo em seus comentários sempre certeiros a cerca do problema que de longos anos presta suporte nesta comunidade . Voce de fato é muito bom
Porém , posso não esta lhe entendendo e vice versa , por isto comentei a cerca do projeto ( simples )
Sobre a ajuda ao amigo Helio
agora tentando ajudar nosso colega: isso tudo que está sendo discutido aqui, bem como o exemplo que eu passei, só funcionam em NestedDatasets e no evento BEFOREUpdateRecord (ANTES DE GRAVAR). No AFTER não vai resolver.
Alem disso o mais importante que eu não comentei antes porque fiz a pergunta Primeiro... No NestedDatasets é so um Provider do Mestre que é responsável por tudo .. ele disse que estava utilizando o provider do Filho
Emerson Nascimento
02/07/2011
eu estava falando em relação ao [url=https://www.devmedia.com.br/forum/delphi/395811-Master-Detail-com-incremento-negativo.html]tópico que você passou[/url] para o colega. transcrevo o trecho que eu estou comentando:
procedure TDm_Dados.dtsfornecedorBeforeUpdateRecord(Sender: TObject;
SourceDS: TDataSet; DeltaDS: TCustomClientDataSet; UpdateKind: TUpdateKind;
var Applied: Boolean);
{$J+}
const
Id: Integer = -1;
{$J-}
begin if (UpdateKind = ukInsert) then
begin
if (SourceDS = sqlfornecedor) then
begin
if (DeltaDS.FieldByName('CHAVE_FORNECEDOR').Value < 0) then
begin
Id := GenID('FORNECEDOR');
DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue := Id;
end;
end
else
if (SourceDS = sqlcontato) then
if (DeltaDS.FieldByName('CHAVE_CONTATO').Value < 0) then
begin
if DeltaDS.FieldByName('CHAVE_FORNECEDOR').Value < 0 then
DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue := Id;
DeltaDS.FieldByName('CHAVE_CONTATO').NewValue := GenID('CONTATO');
end;
end;
end; no meu entender, é obtido o ID da tabela pai (no caso, fornecedor) e guardado na variável Id, correto?
depois essa variável é usada para relacionar os registros filhos, como se vê no trecho:
if DeltaDS.FieldByName('CHAVE_FORNECEDOR').Value < 0 then
DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue := Id;
foi isso que eu disse que não é necessário pois, se estou incluindo um registro na tabela filho, basta atribuir o valor do ID da tabela pai, sem qualquer análise.
por isso sugeri, na parte que manipula o Dataset filho:
DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue :=
DeltaDS.DatasetField.Dataset.FieldByName('CHAVE_FORNECEDOR').Value;
ou seja: independe do valor que já estiver gravado ali, eu coloco o valor que está na tabela pai.
eu estava falando daquele tópico e não deste aberto aqui pelo nosso colega Hélio.
entendido?
exatamente. o código DEVE ficar no Provider da tabela PAI, pois num NestedDataset só há o Provider do pai, correto?
P.S.: que 'editor de textos' FDP que a devmidia foi implantar no fórum, hein?
Helio Souza
02/07/2011
SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
UpdateKind: TUpdateKind);
begin
if (UpdateKind = ukInsert) then
begin
if (SourceDS = sqlClientes) then //TABELA CLIENTE TABELA PAI
DeltaDS.FieldByName('CLI_CODIGO').NewValue := ('GEN_CADCLIENTECODIGO_ID') //O CAMPO CLI_CODIGO É ONDE É GERADO O CODIGO DO CLIENTE PELO GENERATOR
else
if (SourceDS = sqlitemprecocliente) then //TABELA ONDE VAR O PREÇO DO PRODUTO POR CIENTE TABELA FILHO
begin
DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue := ('GEN_CADITEMPRECOCLIENTE_ID'); //O CAMPO ITEMPRE_CODCLIENTE É ONDE VAI RECEBER O CODIGO DO CLIENTE GERADO PELO GENERATOR
// aqui atribui o ID da tabela pai, sem necessidade de qualquer análise //O GEN_CADITEMPRECOCLIENTE_ID ELE É UM GENERATOR SÓ PARA RECEBER O CAMPO GEN_CADCLIENTECODIGO_ID
DeltaDS.FieldByName('CLI_CODIGO').NewValue := //AQUI É NEWVALUE //FOI CRIADO NORMALMENTE IGUAL AOS DEMAIS ESTA CERTO COMO FOI CRIADO
DeltaDS.DatasetField.Dataset.FieldByName('CLI_CODIGO').Value; //AQUI É VALUE
end;
end;
end;
não entendi:
qual é o campo ID da tabela filha?
qual é o campo que relaciona a tabela filha com a tabela pai ?
Emerson Nascimento
02/07/2011
então deixe-me entender e estrutura das suas tabelas.
uma modelagem correta deveria ser mais ou menos assim:
CLIENTE
--------------
ID_CLIENTE (PK) (o generator deve ser aplicado aqui)
COD_CLI (UK) (qualquer numerador que você quiser)
NOME
etc
PRODUTOCLIENTE
--------------
ID_PRODUTOCLIENTE (PK) (o generator deve ser aplicado aqui)
ID_CLIENTE (FK) (este campo relaciona a tabela PRODUTOCLIENTE com a tabela CLIENTE)
ID_PRODUTO (FK) (este campo relaciona a tabela PRODUTOCLIENTE com a tabela PRODUTO)
PRECO
etc
PRODUTO
--------------
ID_PRODUTO (PK) (o generator deve ser aplicado aqui)
CODIGO (UK) (qualquer numerador que você quiser)
DESCRICAO
ALIQICMS
ALIQIPI
PRECO
etc
então você tem um generator em cada tabela, com um campo em cada uma delas para identificação do REGISTRO e, na tabela PRODUTOCLIENTE, você tem um campos que fazem o relacionamento entre as tabelas.
entendido?
depois você deve fazer a implementação proposta no DatasetProvider da tabela PAI, desde que use NestedDatasets (se não sabe o que é isso, pergunte). E deve ser feito no BEFOREUpdate.
então, seguindo a estrutura acima, o evento seria (note que é BeforeUpdateRecord, e na tabela PAI):
procedure Tdmvendas.dspClienteBeforeUpdateRecord(Sender: TObject;
SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
UpdateKind: TUpdateKind);
begin
if (UpdateKind = ukInsert) then
begin
if (SourceDS = sqlClientes) then // TABELA CLIENTE (PAI)
DeltaDS.FieldByName('ID_CLIENTE').NewValue := GEN_ID('GEN_CLIENTE_ID'); // GERA O IDENTIFICADOR UNICO DO REGISTRO
else
if (SourceDS = sqlitemprecocliente) then // TABELA PRODUTO x CLIENTE (FILHO)
begin
DeltaDS.FieldByName('ID_PRODUTOCLIENTE').NewValue := GEN_ID('GEN_PRODUTOCLIENTE_ID'); // GERA O IDENTIFICADOR UNICO DO REGISTRO
// aqui atribui o ID da tabela pai para que seja feito o relacionamento entre as tabelas
DeltaDS.FieldByName('ID_CLIENTE').NewValue := // id_cliente da tabela filho
DeltaDS.DatasetField.Dataset.FieldByName('ID_CLIENTE').Value; // igual ao id_cliente da tabela pai
end;
end;
end;
Marco Salles
02/07/2011
Rapaz eu estou passando muita raica com isto.. Não da para CITAR , não da Para IDENTAR , não da para COMENTAR
As mensagens ficam fora de ORDEM .. Enfim , ningume se entende e uma coisa TRIVIAl , como é o caso da dúvida do amigo
Helio , fica arrastando para a eternidade
Enfim , vamos ver se chegumemos a um acordo
Este Modelo que ele não sei porque foi levado a praticar ( utilizando o AfterUpdateRecord ) não vai dar certo
Primeiro que o evento seria o BeforeUpdateRecord e não o AfterUpdateRecord
segundo.... Isto so funciona se existir um ID previamente cadastrado , como eu cansei exaustivamente
de comentar nos THREADS anteriores . Ou se dar um applayUpdates a cada post (neste caso pode ser que sim)
mas perder um encanto da tecnologia que é trabalhar desconectado
COmo ele obtem o ID quando esta simplesmente cadastradando um Item da Tabela PAI ??????
Como se ao dar um Insert na tabela Filha ele ira pedir o Id da Tabela Pai ????
Pelo que estou entendendo esta deixando para o Evento BeforeUpdateRecord , Mas ele precisa deste valores inicialmente em Memoria (nos CDS...) A Cada Insert eu preciso ter este VAlor....
Eu desconheço este Modelo , e queria também entender , fazendo um NestedDataSet utilizando o BeforeUpdateREcord sem os Ids previamentes cadastradados ????
Enfim , acho que ele esta ficando maluco ( nos e o Editor do Delphi ) o estamos Deixando maluco. Ate eu estou ficando maluco
Helio Souza
02/07/2011
Helio Souza
02/07/2011
SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
UpdateKind: TUpdateKind; var Applied: Boolean);
begin
if (UpdateKind = ukInsert) then
begin
if (SourceDS = sqlClientes) then // TABELA CLIENTE (PAI)
DeltaDS.FieldByName('CLI_CODIGO').NewValue := ('GEN_CADCLIENTECODIGO_ID') // GERA O IDENTIFICADOR UNICO DO REGISTRO
else
if (SourceDS = sqlitemprecocliente) then // TABELA PRODUTO x CLIENTE (FILHO)
begin
DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue := ('GEN_CADITEMPRECOCLIENTE_ID'); // GERA O IDENTIFICADOR UNICO DO REGISTRO
// aqui atribui o ID da tabela pai para que seja feito o relacionamento entre as tabelas
DeltaDS.FieldByName('CLI_CODIGO').NewValue := // id_cliente da tabela filho
DeltaDS.DatasetField.Dataset.FieldByName('CLI_CODIGO').Value; // igual ao id_cliente da tabela pai
end;
end;
end; eu uso edits para buscar o produto é quando saio do ultimo campo ele manda o produto para um dbgrid. procedure Tfrmcadcliente.comissao_itempreExit(Sender: TObject);
begin
//esse codigo verifica se tem item repetido, no dbgrid
if dmvendas.dsitemprecocliente.DataSet.Locate('ITEMPRE_CODPRODUTO',codproduto_itempre.Text,[]) then
begin
Application.MessageBox('Produto não pode ser inserido, já Digitado anteriormente!','Cadastro de Cliente',MB_OK+MB_ICONEXCLAMATION);
codproduto_itempre.SetFocus;
codproduto_itempre.Clear;
descricao_itempre.Clear;
valor_itempre.Clear;
comissao_itempre.Clear;
Abort;
end;
//fim do codigo
dmVendas.cdsitemprecocliente.Insert;
DBGrid1.Fields[0].AsString := codproduto_itempre.Text;
DBGrid1.Fields[1].AsString := valor_itempre.Text;
DBGrid1.Fields[2].AsString := comissao_itempre.Text;
DBGrid1.Fields[3].AsString := codrep_cli.Text; codproduto_itempre.SetFocus;
end;
Emerson Nascimento
02/07/2011
procedure Tdmvendas.dspClientesBeforeUpdateRecord(Sender: TObject;
SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
UpdateKind: TUpdateKind; var Applied: Boolean);
begin
if (UpdateKind = ukInsert) then
begin
if (SourceDS = sqlClientes) then // TABELA CLIENTE (PAI)
DeltaDS.FieldByName('CLI_CODIGO').NewValue := GEN_ID('GEN_CADCLIENTECODIGO_ID') // GERA O IDENTIFICADOR UNICO DO REGISTRO
else
if (SourceDS = sqlitemprecocliente) then // TABELA PRODUTO x CLIENTE (FILHO)
// aqui atribui o ID da tabela pai para fazer o relacionamento entre as tabelas
DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue :=
DeltaDS.DatasetField.Dataset.FieldByName('CLI_CODIGO').Value; // igual ao id_cliente da tabela pai
// lembro que essa forma não é usual. toda tabela deveria ter um campo identificador
end;
end;
agora só precisamos saber se você está aninhando os datasets (NestedDatasets).
o que é isso??? é quando você faz o relacionamento a partir do dataset e não do clientdataset. com isso as tabelas filhas são campos especiais da tabela pai.
assim:
SQLDatasetCliente (pai)
DatasourceCliente
DatasetProviderCliente
SQLDatasetProdCli (filho)
o detalhe é que a instrução do SQLDataset filho precisa ter um parâmetro que irá receber o relacionamento da tabela pai.
algo assim:
SELECT * FROM CADITEMPRECOCLIENTE WHERE ITEMPRE_CODCLIENTE = :CLI_CODIGO
ATENÇÃO! o parâmetro precisa ter o mesmo nome do campo da tabela pai. é preferível até que os campos tenham o mesmo nome em ambas tabelas.
aí você faz a ligação entre esses datasets assim:
DatasourceCliente.Dataset = SQLDatasetCliente
SQLDatasetProdCli.Datasource = DatasourceCliente -> é aqui que a mágica acontece
DatasetProviderCliente.Dataset = SQLDatasetCliente
pronto. foi feito o relacionamento entre as tabelas.
agora você precisa preparar os ClientDatasets para receberem esse aninhamento de datasets.
instancie dois ClientDatasetse dois Datasources. vou chamá-los de cdsCliente e cdsProdCli.
agora configurando os clientdatasets:
cdsCliente.ProviderName = DatasetProviderCliente
dependendo da sua forma de acesso, será necessário preencher também a propriedade RemoteServer ou a propriedade ConnectionBroker.
dê um duplo-clique no cdsClientes e traga todos os campos.
entre eles deverá existir um campo chamado cdsClienteSQLDatasetProdCli. esse é o campo especial que eu citei acima. ele é um campo do tipo TDatasetField e na verdade é a tabela filho.
agora, no cdsProdCli, faça a ligação da propriedade DatasetField à esse campo citado. pronto. seu NestedDataset está pronto.
agora, implemente a propriedade BeforeUpdateRecord do DatasetProviderCliente com aquele código do topo deste mensagem. assim os valores dos GENERATORs serão atribuídos ao executar o método ApplyUpdates.
LEMBRANDO QUE O APPLYUPDATES DEVE SER EXECUTADO SOMENTE NO CLIENTDATASET PAI.
pra finalizar você precisa atribuir valores fictícios ao campos de identificação no momento da inserção dos registros, para poder trabalhar nos registros em memória, até que eles recebam a atribuição definitiva.
então, no cdsCliente, implemente o AfterInsert de modo a obter um valor qualquer (somente para poder relacionar com a tabela filho).
algo assim:
procedure TForm14.cdsClienteAfterInsert(Dataset: TDataset);
begin
cdsClienteCLI_CODIGO.AsInteger := GetTickCount; // função na unit Windows.
end;
dessa forma, ao incliur um registro no cdsCliente, será atribuído automaticamente um valor ao compo ID.
trabalhando dessa forma você só precisa executar o ApplyUpdates ao confirmar toda a digitação.
bom, espero que agora tudo se resolva.
Helio Souza
02/07/2011
SELECT * FROM CADITEMPRECOCLIENTE WHERE ITEMPRE_CODCLIENTE = :CLI_CODIGO
SELECT * FROM CADITEMPRECOCLIENTE WHERE CLI_CODIGO = :CLI_CODIGO
Marco Salles
02/07/2011
Helio Souza
02/07/2011
Marco Salles
02/07/2011
Helio Souza
02/07/2011
Helio Souza
02/07/2011
Helio Souza
02/07/2011
Marco Salles
02/07/2011
Helio Souza
02/07/2011
DeltaDS.FieldByName('CLI_CODIGO').NewValue := ('GEN_CADCLIENTECODIGO_ID')procedure Tdmvendas.dspClientesBeforeUpdateRecord(Sender: TObject;
SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
UpdateKind: TUpdateKind; var Applied: Boolean);
begin
if (UpdateKind = ukInsert) then
begin
if (SourceDS = sqlClientes) then // TABELA CLIENTE (PAI)
DeltaDS.FieldByName('CLI_CODIGO').NewValue := ('GEN_CADCLIENTECODIGO_ID') // GERA O IDENTIFICADOR UNICO DO REGISTRO
else
if (SourceDS = sqlitemprecocliente) then // TABELA PRODUTO x CLIENTE (FILHO)
begin
DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue := ('GEN_CADITEMPRECOCLIENTE_ID'); // GERA O IDENTIFICADOR UNICO DO REGISTRO
// aqui atribui o ID da tabela pai para que seja feito o relacionamento entre as tabelas
DeltaDS.FieldByName('CLI_CODIGO').NewValue := // id_cliente da tabela filho
DeltaDS.DatasetField.Dataset.FieldByName('CLI_CODIGO').Value; // igual ao id_cliente da tabela pai
end;
end;
end;Helio Souza
02/07/2011
Helio Souza
02/07/2011
Emerson Nascimento
02/07/2011
preste atenção no trecho abaixo e veja se você está gerando corretamente o id.
repare bem na linha que você publicou e na linha que eu sugeri. a diferença é sutil, mas vou deixar em negrito para ficar mais fácil de você identificar:
seu código:
if (SourceDS = sqlClientes) then // TABELA CLIENTE (PAI)
DeltaDS.FieldByName('CLI_CODIGO').NewValue := ('GEN_CADCLIENTECODIGO_ID') // GERA O IDENTIFICADOR UNICO DO REGISTRO
o correto:
if (SourceDS = sqlClientes) then // TABELA CLIENTE (PAI)
DeltaDS.FieldByName('CLI_CODIGO').NewValue := GEN_ID('GEN_CADCLIENTECODIGO_ID') // GERA O IDENTIFICADOR UNICO DO REGISTRO
seu código:
if (SourceDS = sqlitemprecocliente) then // TABELA PRODUTO x CLIENTE (FILHO)
begin
DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue := ('GEN_CADITEMPRECOCLIENTE_ID'); // GERA O IDENTIFICADOR UNICO DO REGISTRO
o correto:
if (SourceDS = sqlitemprecocliente) then // TABELA PRODUTO x CLIENTE (FILHO)
begin
DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue := GEN_ID('GEN_CADITEMPRECOCLIENTE_ID'); // GERA O IDENTIFICADOR UNICO DO REGISTRO
outra coisa: não esqueça de acrescentar poPropagateChanges nas opções do DatasetProvider. com isso o ClientDataset receberá o valor atribuído ao ID e você não precisará dar Refresh no nele.
Helio Souza
02/07/2011
Marco Salles
02/07/2011
Marco Salles
02/07/2011
Helio Souza
02/07/2011
QryId.SQL.Add('SELECT GEN_ID(' + Indice +',1) AS ID_RET FROM RDB$DATABASE');
Function TDm.RetornaChaveFirebird(Indice: String): Integer;
var
{Declarando a query que sera utilizada para pegar o valor gerado.}
QryId: TSQLQuery;
begin
{Instanciado o objeto.}
QryId := TSQLQuery.Create(nil);
try
{Preparando a query para a execução}
QryId.SQLConnection := Conexao;
{SQL que irá pegar o registro gerado, dado onome do generator da tabela.}
QryId.SQL.Add('SELECT GEN_ID(' + Indice +',1) AS ID_RET FROM RDB$DATABASE');
QryId.Open;
if QryId.IsEmpty then
{Não ocorreu geração, por isso o cadastro não pode continuar.}
raise Exception.Create('Não foi criado nenhum valor.')
else
{Retornando o valor gerado.}
result := QryId.FieldByName('ID_RET').AsInteger;
finally
{Apos o uso da query, liberando a memoria.}
QryId.Free;
end;
end;
Emerson Nascimento
02/07/2011
if (SourceDS = sqlClientes) then // TABELA CLIENTE (PAI)
DeltaDS.FieldByName('CLI_CODIGO').NewValue := RetornaChaveFirebird('GEN_CADCLIENTECODIGO_ID');
Helio Souza
02/07/2011
Marco Salles
02/07/2011
Por acaso vc esta usando isto ???
procedure TForm14.cdsClienteAfterInsert(Dataset: TDataset);
begin
cdsClienteCLI_CODIGO.AsInteger := GetTickCount; // função na unit Windows.
end;
Helio Souza
02/07/2011
Helio Souza
02/07/2011