Fórum Auto incremento com sum max? #424061

19/09/2012

0

Boa noite a todos,


Eu gostaria de saber a melhor maneira de implementar um auto incremento no Firebird.

Eu estou fazendo ate o momento com Generators. Estou fazendo alguns testes e não gostei muito.

Quando tento gravar no banco e da algum erro mesmo assim ele gera um novo numero, isso faz com que eu fique
com buracos na numeração do meu banco.

No delphi eu uso o seguinte Código no evento BeforeUpdate Record do DataSetProvider.

   if UpdateKind = ukInsert then
    begin
        if SourceDs = sdsCor then
            begin
               sdsGenCor.Open;
            try
             id := sdsGenCor.FieldByName('NOVOID').AsInteger;
            finally
              sdsGenCor.Close;
            end;
               DeltaDs.FieldByName('CODI_COR').NewValue :=id;
           end;
        end;
    end;


onde id é uma variavel que recebe valor 0 na criação do datamodule e é decrementada para passar o valor para o
campo chave da tabela.

Eu tava querendo implementar alguma coisa com select max no evento AfterPost do ClientDataSet.

Queria fazer tipo pra gerar um novo valor só se nao der nem um erro no ApplyUpdates e se todos os campos obrigatorios
estiverem preenchidos ai sim gera o codigo.

So não estou sabendo montar essa instrução.

Alguem tem alguma ideia?
Um exemplo pra me passar?

Eu não quero é deixar furos na numeração.
Willian Amor

Willian Amor

Responder

Posts

19/09/2012

Alisson Santos

A melhor opção e mais limpa é utilizar o generator com trigger, agora se quiser controlar via programação vai ter que criar uma função para isso.
Responder

Gostei + 0

19/09/2012

Deivison Melo

Dependendo da sua versão do firebird além do controle feito por Generator´s e trigge´s (para alimentar os generator´s), você pode utiliza as sequences (versões mais atuais do firebird).

Ou ainda pode utilizar a função abaixo:


function GeneratorID (aName: string; Connection: TSQLConnection;
Incrementa: Boolean): integer;
var
Qry: TSQLQuery;
begin
Qry := TSQLQuery.Create(nil);
try
Qry.SQLConnection := Connection;
if Incrementa then
Qry.SQL.Add(
'SELECT GEN_ID('+aName+', 1) FROM RDB$DATABASE')
else
Qry.SQL.Add(
'SELECT GEN_ID('+aName+', 0) FROM RDB$DATABASE');
Qry.Open;
Result := Qry.Fields[0].AsInteger;
finally
FreeAndNil(Qry);
end;
end;
Responder

Gostei + 0

19/09/2012

Claudia Nogueira

A forma correta é realmente generator + trigger.
Tem um artigo meu publicado no firebase sobre esse assunto e espero que seja útil.
Qualquer dúvida pode perguntar.

http://www.firebase.com.br/fb/artigo.php?id=2396
Responder

Gostei + 0

20/09/2012

Willian Amor

Vou dar uma olhada nesse que vocês me passaram,

quero uma maneira onde não gere um numero se der erro antes de gravar.

Que so gere um novo valor se realmente o registro for gravado no banco.

Responder

Gostei + 0

20/09/2012

Willian Amor

A forma correta é realmente generator + trigger.
Tem um artigo meu publicado no firebase sobre esse assunto e espero que seja útil.
Qualquer dúvida pode perguntar.

http://www.firebase.com.br/fb/artigo.php?id=2396



Eu ja havia tentado assim também, porém se der erro na hora de gravação ainda sim ele gera um novo número,

tipo inicializa em zero, o generator ganha o valor 1 e da erro na gravação. Depois se eu for na tabela ela nao tera o
valor 1 ja vai pular para o 2.


function GeneratorID (aName: string; Connection: TSQLConnection;
Incrementa: Boolean): integer;
var
Qry: TSQLQuery;
begin
Qry := TSQLQuery.Create(nil);
try
Qry.SQLConnection := Connection;
if Incrementa then
Qry.SQL.Add(
'SELECT GEN_ID('+aName+', 1) FROM RDB$DATABASE')
else
Qry.SQL.Add(
'SELECT GEN_ID('+aName+', 0) FROM RDB$DATABASE');
Qry.Open;
Result := Qry.Fields[0].AsInteger;
finally
FreeAndNil(Qry);
end;
end;




Eu estava querendo montar o seguinte, uma função realmente.

Por exemplo eu tenho uma função que verifica se os campos obrigatorios foram quando o usuario clica em salvar.
Se foram ele grava, do contrario avisa ao usuario que o campo "TAL" deve ser informado.

O problema é assim. Se passar por essa função mas se der erro no ApplyUpdates(<> 0), ele retorno um erro,
e da maneira como estou fazendo hoje, mesmo dando erro no ApplyUpdates ele gera um novo numero para o Generator,
ai eu perco um numero, fica um furo no meu banco. Eu queria ver se consigo montar algo no beforePost onde
eu ia verificar se ApplyUpdates (= 0) e se os campos obrigatorios foram preenchidos.

Se passar por essas informações ai sim geraria um novo codigo.

Alguem pode me dar uma ideia?
Responder

Gostei + 0

20/09/2012

Claudia Nogueira

Willian eu trabalho com transação, sem ApplyUpdates, então sempre se em um bloco de operações ocorrer um erro (exception) toda a operação é cancelada, sendo assim não incrementa o generator se utilizar o trigger.

Exemplo:

Var
  iCodigo : Integer;
begin
  try
    IBQuery1.Close;
    IBQuery1.SQL.Text := 'INSERT INTO cidade (id_cidade, nome_cidade,  uf) VALUES (0, :nome, :uf)';
    IBQuery1.ParamByName('nome').AsString := 'SÃO PAULO';
    IBQuery1.ParamByName('uf').AsString := 'SP';
    IBQuery1.ExecSQL;
    iCodigo := 0;
    IBQuery1.Close;
    IBQuery1.SQL.Text := 'SELECT GEN_ID(GEN_ID_CIDADE, 0) codigo FROM RDB$DATABASE';
    IBQuery1.Open;
    if not IBQuery1.IsEmpty then
      iCodigo := IBQuery1.FieldByName('codigo').AsInteger;
    IBTransaction1.CommitRetaining;
  except
    IBTransaction1.RollbackRetaining;
  end;
end;


Coloquei aquele iCodigo recebendo o valor do último generator para caso você esteja querendo fazer várias operações, e as inserções subsequentes estiverem ligadas por uma chave com a primeira tabela.
Responder

Gostei + 0

20/09/2012

Alisson Santos

No caso isso está ocorrendo pois não está abordando o processo, experimente colocar um abort depois que der o erro.
Geralmente eu faço rotina direto no provide dataset abortando o processo.
Responder

Gostei + 0

21/09/2012

Willian Amor

Willian eu trabalho com transação, sem ApplyUpdates, então sempre se em um bloco de operações ocorrer um erro (exception) toda a operação é cancelada, sendo assim não incrementa o generator se utilizar o trigger.

Exemplo:

Var
  iCodigo : Integer;
begin
  try
    IBQuery1.Close;
    IBQuery1.SQL.Text := 'INSERT INTO cidade (id_cidade, nome_cidade,  uf) VALUES (0, :nome, :uf)';
    IBQuery1.ParamByName('nome').AsString := 'SÃO PAULO';
    IBQuery1.ParamByName('uf').AsString := 'SP';
    IBQuery1.ExecSQL;
    iCodigo := 0;
    IBQuery1.Close;
    IBQuery1.SQL.Text := 'SELECT GEN_ID(GEN_ID_CIDADE, 0) codigo FROM RDB$DATABASE';
    IBQuery1.Open;
    if not IBQuery1.IsEmpty then
      iCodigo := IBQuery1.FieldByName('codigo').AsInteger;
    IBTransaction1.CommitRetaining;
  except
    IBTransaction1.RollbackRetaining;
  end;
end;


Coloquei aquele iCodigo recebendo o valor do último generator para caso você esteja querendo fazer várias operações, e as inserções subsequentes estiverem ligadas por uma chave com a primeira tabela.


Eu to usando DBExpress.Eu achei que o Provider já cuidava da transação.

No caso isso está ocorrendo pois não está abordando o processo, experimente colocar um abort depois que der o erro.
Geralmente eu faço rotina direto no provide dataset abortando o processo.


Posso tentar mas quando da erro de Applyupdates ele nao grava nada. Ja aborta a transação. Mas como ele fez a tentativa
de mandar os dados para o banco já gerou um novo numero para o generator.
Responder

Gostei + 0

24/09/2012

Willian Amor

Bom apos realizar alguns testes resolvi usar o Select Max mesmo.
inicialei uma variavel na criação do meu datamodule e no onnewrecord do ClientDataSet eu a decremento para 0.

entao no beforepost do CDS eu verifico se ele for menor que 1 executa o select max, se nao ele deixa o numero que ja está la.

Obrigado a todos....
Responder

Gostei + 0

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar