Ajuda em montar código

Delphi

26/11/2012

Ola galera do DevMedia....estou eu novamente aqui pedindo ajuda em montar um código para evitar Key Violation. O banco de dados é PIRADOX, o campo de código é alfanumérico, o sistema funciona em rede e dá Key Violation quando duas máquinas tentam cadastrar um serviço ao mesmo tempo. Uma grava e a outra dá erro. Tentei montar de varias formas (sou iniciante e to aprendendo na raça mesmo) mas nenhuma me deu um resultado satisfatório, o que chegou mais perto fica saltando um valor tipo, pula do 000130 para o 000132 e assim vai.

O código, se possível, seria para reconhecer se vai dar o erro, se sim, pegaria o último valor da tabela e adicionaria 1 e gravaria. Na internet achei esse código e tentei coloca-lo funcional para mim, mas dá erro de compatibilidade entre String e Integer já que o campo de código é alfanumerico.

try
dm01.tbl_servico.Post;
except
on E:EDBEngineError do
if E.Errors[0].ErrorCode = 9729 then
dm01.tbl_servicoOS_COD.AsInteger := dm01.tbl_servicoOS_COD.AsInteger + 1;
end;


Alguém pode me ajudar?
Carlos Magno

Carlos Magno

Curtidas 0

Respostas

Luiz Menin

Luiz Menin

26/11/2012

Tu poderia realizar uma consulta ao clicar no botão salvar, por exemplo.
Adicione um TQuery.
SQLQuery.Close;
SQLQuery.SQL.Clear;
SQLQuery.SQL.Add('SELECT MAX(CODIGO) AS TOTAL FROM TABELA');
SQLQuery.ExecSQL();
SQLQuery.Open;
iMaxCod := SQLQuery.FieldByName('TOTAL').AsInteger + 1;


Atribui o valor da variavel criada (iMaxCod) ao campo código.
Espero ter ajudado.
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Luis...não testei o seu modo, mas tentei esse outro modo. Criei um dataset para a mesma tabela nomeando-a para servaux. Na compilação ele passou, mas quando cliquei no botão salvar congelou tudo. Veja o código e me diz onde posso estar errando:

var
codi:String[6];
begin
dm01.tbl_servaux.Last;
dm01.tbl_servicoOS_COD.AsString := inttostr(dm01.tbl_servauxOS_COD.AsInteger + 1);
while Length(dm01.tbl_servicoOS_COD.AsString) <6 do
codi:='0' + dm01.tbl_servicoOS_COD.AsString;
dm01.tbl_servico.Post;
GOSTEI 0
Luiz Menin

Luiz Menin

26/11/2012

Pode informar que tipo de erro ocorreu?
GOSTEI 0
Luiz Menin

Luiz Menin

26/11/2012

Não recomendo utilizar este seu método, pois caso você utilize qualquer tipo de ordenação diferente da por chave primaria, você provavelmente vai enfrentar problemas.
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

O erro que ocorreu foi justamente que congelou a compilação, tive de encerrar usando o Ctrl-Alt-Del, e o Delphi, mesmo dando o stop, não retornou para o modo de codificação.
GOSTEI 0
Claudia Nogueira

Claudia Nogueira

26/11/2012

Esse while está em loop infinito, pois você usou o length no dm01.tbl_servicoOS_COD.AsString e ele não altera dentro do while.

codi := dm01.tbl_servicoOS_COD.AsString;
while Length(codi ) <6 do
codi:='0' + dm01.tbl_servicoOS_COD.AsString;


Luis...não testei o seu modo, mas tentei esse outro modo. Criei um dataset para a mesma tabela nomeando-a para servaux. Na compilação ele passou, mas quando cliquei no botão salvar congelou tudo. Veja o código e me diz onde posso estar errando:

var
codi:String[6];
begin
dm01.tbl_servaux.Last;
dm01.tbl_servicoOS_COD.AsString := inttostr(dm01.tbl_servauxOS_COD.AsInteger + 1);
while Length(dm01.tbl_servicoOS_COD.AsString) <6 do
codi:='0' + dm01.tbl_servicoOS_COD.AsString;
dm01.tbl_servico.Post;
GOSTEI 0
Carlos Bernardo

Carlos Bernardo

26/11/2012

try
dm01.tbl_servico.Post;
except
on E:EDBEngineError do
if E.Errors[0].ErrorCode = 9729 then
dm01.tbl_servicoOS_COD.AsInteger := dm01.tbl_servicoOS_COD.AsInteger + 1;
end;


Amigo vamos tentar assim :

Cria uma funcao que retorne o proximo numero livre...

function retorna_maxCodigo : string;
var
  QRY :TQuery;
begin
  Qry := TQuery.Create(self);
  with qry do
  TRY
    DatabaseName := SEU_ALIAS;
    sql.Add('Select max(OS_COD) as CODMAX from nomedatabela');
    Open;
    Result := formatfloat('000000',Qry.FieldByName('CODMAX').Asfloat + 1);
  FINALLY
    QRY.free;
  END; 
end;



antes de dar o POST vc usa assim

dm01.tbl_servicoOS_COD.AsString := retorna_maxCodigo;
dm01.tbl_servicos.POST;

A unit DBTables, tem que estar declarada na clausula Uses
GOSTEI 0
Alisson Santos

Alisson Santos

26/11/2012

Bom vamos a regra, o campo que vai receber esse valor é inteiro.
Se ele for campo inteiro como creio que seja ele não vai gravar os zeros na frente então ao invés de ficar 000123 ele vai aparecer apenas o 123, para que ele apareça os zeros na frente o campo tem que ser string.

Da maneira que nosso amigo acima está informando não precisa colocar os zeros e sim apenas trazer como resultado o campo adicionando +1.
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Carlos, ou diria, xará, rsrsrs.....deu erro na linha abaixo:
Qry := TQuery.Create(self);

Erro reportado: Undeclared indentifier: 'self';
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Alisson....o campo não é inteiro...é alfanumérico
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Galera, achei uma forma de chegar mais perto onde eu quero. Usei uma TQuery para pegar o MAX, adicionar mais um 1 e gravar. Só que ao gravar, só vai o número gerado, tipo assim, se é para gerar o número 000025, só vai o 25 sem os 0000.

Como faço para adicionar os zeros?

Segue o código, favor verificar se esta certo:
dm01.qr_servcodMAXOFOS_COD.AsString;
dm01.tbl_servicoOS_COD.AsString := IntToStr(dm01.qr_servcodMAXOFOS_COD.AsInteger + 1);
dm01.tbl_servico.Post;
GOSTEI 0
William

William

26/11/2012

Tenta assim:

dm01.tbl_servicoOS_COD.AsString := FormatFloat('0000', dm01.qr_servcodMAXOFOS_COD.AsInteger + 1);
GOSTEI 0
Carlos Bernardo

Carlos Bernardo

26/11/2012

Desculpa

troca essa linha Qry := TQuery.Create(self);
por essa Qry := TQuery.Create(nil);
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

William, sua dica funcionou legal, agora so falta fazer o teste em rede com duas máquinas efetuando cadastros ao mesmo tempo e ver se não dá erro de KEY VIOLATION.

Assim que concluir os testes eu retorno para dizer se deu certo ou não.
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

William...gravou, mas ainda deu erro de KEY VIOLATION.
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Modifiquei o código e consegui remover o erro key violation, mas ao tentar gravar nas duas maquinas simultaneamente, uma delas grava e o registro da outra se perde. Haveria uma forma de não perder esse registro ao gravar?
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Ninguem?
GOSTEI 0
Luiz Menin

Luiz Menin

26/11/2012

Como assim "perdendo"? Ele executa a função de salvar mas não grava no banco? Ocorre algum erro? Qual a função está sendo utilizada?
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Caro Luis...quando as duas maquinas vão cadastrar ao mesmo tempo, umas delas grava e a outra não...na hora não aparece o erro de KEY VIOLATION, somente após uma nova tentativa de cadastro (errei em um post atrás dizendo que não estava mais dando o erro, me perdoem). No datamodule eu criei uma TQuery para buscar o último número cadastrado com um MAX e chamo ele conforme o código abaixo, não se esta correto, pois sou iniciante e to aprendendo na raça somente com ajuda de vocês e das minhas próprias tentativas:

dm01.qr_servcod.Open; //abre a query
dm01.qr_servcod.Refresh; //dá um refresh para buscar o último número
dm01.qr_servcodMAXOFOS_COD.AsString;
dm01.tbl_servicoOS_COD.AsString := FormatFloat('000000',dm01.qr_servcodMAXOFOS_COD.AsInteger + 1); //adiciona mais 1 ao último número
dm01.tbl_servico.Post; //grava
dm01.qr_servcod.Close; //fecha a query
GOSTEI 0
Luiz Menin

Luiz Menin

26/11/2012

Tente assim:
var
  iCont : Integer;
begin
dm01.qr_servcod.Open; //abre a query
dm01.qr_servcod.ExecSQL();
iCont := dm01.qr_servcodMAXOFOS_COD.AsInteger + 1;
dm01.tbl_servicoOS_COD.AsString := FormatFloat('000000', iCont); //adiciona mais 1 ao último número
dm01.tbl_servico.Post; //grava
dm01.qr_servcod.Close; //fecha a query
end;
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Caro Luis.....deu o seguinte erro:
Cannot perform this operation on an open dataset


e não gravou nada
GOSTEI 0
Luiz Menin

Luiz Menin

26/11/2012

dm01.qr_servcod.Close;
dm01.qr_servcod.ExecSQL();
dm01.qr_servcod.Open;
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Luis...esta acontecendo assim, a maquina onde fica o banco de dados está gravando normal, mas a maquina em que fica em rede não grava, ela finaliza como se tudo ok pois mostra numa tela para impressão, mas o número gerado fica igual ao gerado pela outra maquina onde fica os BD, e todos os dados se perdem.
GOSTEI 0
Luiz Menin

Luiz Menin

26/11/2012

Ok, então faça o seguinte teste: coloque um DBEdit e insira o código manualmente. Depois posta o resultado.
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

amanha vejo isso porque agora estou de saida do trabalho e posto se deu certo
GOSTEI 0
Luiz Menin

Luiz Menin

26/11/2012

Ok, fico no aguardo.
GOSTEI 0
Carlos Bernardo

Carlos Bernardo

26/11/2012

Amigo, coloque esse comando no evento BeforePost da Table

dm01.tbl_servicoOS_COD.AsString := FormatFloat('000000', dm01.qr_servcodMAXOFOS_COD.AsInteger + 1);

Deve evitar esse erro...
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Luis...mesmo colocando um dbedit e eu modificando o numero, ainda assim a maquina que fica em rede não grava. Será que este outro código que está no botão NOVO pode estar interferindo?
Var
Cod:String[6];
begin
  dm01.tbl_servico.open;
  if dm01.tbl_servico.IsEmpty
  then cod:='000001'
else
begin
  dm01.tbl_servico.Last;
  Cod:=IntToStr(dm01.tbl_servicoOS_COD.AsInteger + 1);
  while Length(Cod)<6 do
  cod:='0'+ Cod;
end;
dm01.tbl_servico.Append;
dm01.tbl_servicoOS_COD.Value:=Cod;
end;


Carlos...vou tentar sua forma, mas não entendi o porque colocar no evento BeforePost se o código acima está justamente antes de concluir o Post

Fico no aguardo....
GOSTEI 0
Luiz Menin

Luiz Menin

26/11/2012

Certo, mas se você executa a função de buscar o maior código no botão salvar, então se torna dispensável esse teu código.
Não sei como funciona tua aplicação, mas acredito que só o Open e o Append seja suficiente;

dm01.tbl_servico.Open;
dm01.tbl_servico.Append;


Se você utiliza essa formatação no campo código, então utilize somente no botão salvar.
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Retirei aquele código do botão NOVO e deixei apenas o Open e o Append. Mas, não sei porque, a máquina que fica em rede não esta gravando quando trabalha simultaneamente. Só grava quando ela trabalha sozinha.

Aproveitando, como faço para, no botão salvar e após aplicar o Post, dar um refresh na tabela?
GOSTEI 0
Luiz Menin

Luiz Menin

26/11/2012

Se o problema persiste, é porque deve existir alguma restrição em algum dos componentes. Se você quer tirar essa dúvida, faça uma inserção via comando SQL direto da aplicação.

Após o post deve ser atualizada a tabela.
dm01.tbl_servico.Refresh;
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Me perdoe a pergunta, Luis, pois sou leigo ainda nisso.....como que faço isso?
GOSTEI 0
Gilvanio Gonçalves

Gilvanio Gonçalves

26/11/2012

caro colega um post tão enorme para uma rotina tão simples se tratando de paradox(para doidox).

campo codigo de sua tabela alphanumerico, vc deseja que contenha zeros antes do codigo certo;

supondo que vc vai usar o codigo em um botão com o nome novo ou incluir ok?


procedure TForm1.Button1Click(Sender: TObject);
Var
Cod:String[5];{ cria uma variável para incrementar} 
begin
dm01.tbl_servico.Open;
if dm01.tbl_servico.IsEmpty { verifica se a tabela está vazia} 
then cod:='00001'
else
begin
dm01.tbl_servico.Last; { posiciona no último registro da tabela} 
Cod:=IntToStr(dm01.tbl_servicoOS_COD.AsInteger + 1); { pega o valor do último OS_Cod, que é string, considera- o como inteiro, adiciona mais 1 e por fim, armazena na variável Cod invertendo de inteiro para string} 
while Length(Cod)<5 do {enquanto o tamanho de cod for menor que 5, acrescenta.
cod:='0'+ Cod; // um zero a esquerda de cod}
end;
dm01.tbl_servico.Append; {inclui um registro em branco no final da tabela }
dm01.tbl_servicoOS_COD.Value:=Cod;
end;



no botão gravar do mesmo form este codigo:

dm01.tbl_servico.Post;
{ grava na tabela todos os dados de seu form, mais o codigo}
caso precise atualizar os dados para uma grid no caso, apos o comando post use o refresh.

exemplo:
 dm01.tbl_servico.Post;
               dm01.tbl_servico.Refresh;


tendo um botão para cancelar :

dm01.tbl_servico.Cancel;
{cancela a gravação dos dados, e claro do codigo também}


no evento onclose de seu form :

dm01.tbl_servico.Cancel
dm01.tbl_servico.Close;



agora quanto ao erro Key Violation, que é violação na tabela, toda tabela em banco paradox tem que ter
um campo com chave primária obrigatório.

vc pode criar um campo com o nome (cod) type + de autoincremento e em key * marcando como campo primário.
ficando assim este campo como chave primária que é obrigatorio ter em tabela paradox.

caso deseja vc pode colocar o campo OS_COD como primário evita-se dados repeditos. isto fica ao seu critério.

erro de access violation, quando esta tentando gravar um registro com o mesmo codigo na mesma tabela.

bém é isto, pra vc que usa paradox pode usar os codigos sem medo de errar, caso vc tenha outras mensagens de erros
post que iremos te ajudar.

quanto ao uso de query,sql, pelo que li nos exemplos dos colegas e as suas tentativas sem entender o que esta fazendo,
aconselho a vc usar o exemplo como passei que usa-se tabela, depois vc estuda a fundo o uso de query, sql, try, except etc.

at+
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Caro Gilvanio...

O campo OS_COD já esta como primário. O que eu busco é solucionar o problema de duas maquinas utilizarem o mesmo banco em rede simultaneamente e, caso as duas maquinas forem iniciar um cadastro ao mesmo tempo, não venha dar esse erro de KEY VIOLATION.

Um sistema que a loja já usa a algum tempo, observei varias vezes que, ao se iniciar um cadastro em uma das máquinas e a outra também iniciar, gera o mesmo código, mas ao salvar, a máquina que clicou no botão salvar primeiro grava os dados com aquele código e a outra gera um novo número consecutivo ao salvo pela máquina anterior.

Eu até podia pedir ajuda o programador que fez esse sistema pois ele é da minha cidade, o problema é que já faz uns 4 meses que o homem desapareceu do mapa e ninguém consegue contacta-lo.
GOSTEI 0
Gilvanio Gonçalves

Gilvanio Gonçalves

26/11/2012

Caro Gilvanio...

O campo OS_COD já esta como primário. O que eu busco é solucionar o problema de duas maquinas utilizarem o mesmo banco em rede simultaneamente e, caso as duas maquinas forem iniciar um cadastro ao mesmo tempo, não venha dar esse erro de KEY VIOLATION.

Um sistema que a loja já usa a algum tempo, observei varias vezes que, ao se iniciar um cadastro em uma das máquinas e a outra também iniciar, gera o mesmo código, mas ao salvar, a máquina que clicou no botão salvar primeiro grava os dados com aquele código e a outra gera um novo número consecutivo ao salvo pela máquina anterior.

Eu até podia pedir ajuda o programador que fez esse sistema pois ele é da minha cidade, o problema é que já faz uns 4 meses que o homem desapareceu do mapa e ninguém consegue contacta-lo.




o que vc citou no que vc observou esta errado em questão de visualização para o usuario, dois micros
mostrando que a OS que sera gravada é com o codigo 010,e claro que quem gravar primeiro, gravara
010, e esta que gravou ao clicar no botão novo o proximo pra ele será 011, o que este programador fez
é uma rotina que verifica na tabela qual é o ultimo codigo da tabela para poder gravar o proximo
acrescentando mais 1. assim o segundo usuario tem no form o codigo 010, somente como exibição,
mas ao gravar, gravará o 011, pois o anterior gravou o 010, erro somente na exibição do novo
codigo, mas gravação correta, sem key violation.


Quanto ao key violation como te disse é devido estar tentando gravar o mesmo codigo(duplicidade)
na mesma tabela, justamente por ser primario, onde não aceita duplicidade, seria assim :micro 1 ,
esta para gravar o codigo 005, o micro 2 esta no mesmo instante a gravar o mesmo codigo.
crie uma campo em sua tabela com o nome cod type autoincremento e primario.
desmarque a opção * primario do campo OS_COD, assim vc eliminrá o erro key violation.

observe que no codigo que te passei o codigo que será gravado na tabela fica reservado
em uma variavel, ele só sera gravado ao clicar no botão gravar.
sendo assim se um usuario uniciar uma nova os com o codigo 0007 e outro usuario iniciar
uma nova os ao mesmo tempo, todos dois terá o codigo 0007, devido o codigo estar ainda em uma variavel
e não na tabela, provocando o key violation para o ultimo usuario que clicar em gravar, pois como pode observar
todos dois irão gravar o codigo 0007, tendo seu campo OS_COD como primário, que é justamente para evitar
duplicidade.
e vc tirando ele de primario o que vai acontecer , gravação de registro com codigo em duplicidade, sem key violation.


o que pode ser feito é mudar a rotina para reservar o codigo na tabela.

se micro1 iniciou uma nova OS e o ultimo codigo da tabela gravado é 0006, no form exibirá 0007
como sendo o proximo, e na tabela no campo codigo já conterá o codigo 0007, somente o codigo, os outros dados
somente ao clicar em gravar, para caso o usuario cancele a OS antes de gravar o codigo 0007 será removido da tabela.

desta forma se micro1 iniciou uma nova Os, qualquer outros micros que der inicio a uma nova os ao mesmo tempo,
não irá gravar codigo em duplicidade.

espero que tenha entendido.

caso não saiba mudar a rotina, avise que tentaremos te ajudar.

at+
GOSTEI 0
Gilvanio Gonçalves

Gilvanio Gonçalves

26/11/2012

Correção na explicação:

no codigo que te passei(rotina).

vai ter o mesmo procedimento que ve observou nos micros.

vc desmarcaando o campo OS_COD como primario. e criando uma campo cod type autoincremento e * primario
ele só servira para que a tabela contenha um campo primario, como disse em paradox
é nescessario em todas as tabelas um campo primario.
desmarcando o campo OS_COD como primario não haverá erro de key violation.
mas a exibição do codigo do form sera 0005, no micro 1 e 0005 no micro2 ou 3 etc.
sendo que quem gravar primeiro e clicar no botão novo terá o codigo 0006 como o proximo.
a possibilidade de duplicidade é praticamente impossivel, pois sempre um usuario dará o comando
de gravar antes um do outro.
assim, vc pode usar a rotina que te passei que funcionará como vc observou no sistema
do outro programador.

at+
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Caro Gilvanio....nessa tabela, o type do campo OS_COD era autoincrement e já dava erro de KEY VIOLATION. Foi por esse motivo que modifiquei o campo para alfanumerico. Só não sei se fiz burrada ou não...rsrsrsrsrsrs.
GOSTEI 0
Gilvanio Gonçalves

Gilvanio Gonçalves

26/11/2012

Caro Gilvanio....nessa tabela, o type do campo OS_COD era autoincrement e já dava erro de KEY VIOLATION. Foi por esse motivo que modifiquei o campo para alfanumerico. Só não sei se fiz burrada ou não...rsrsrsrsrsrs.


se voce deseja como vi no primeiro post seu que OS_COD contenha zeros a esquerda
e seguindo o exemplo que te passei ele, não pode ser autoincremento amigo,
pois no codigo a rotina é que faz ele ser incrementado sempre com mais 1
e ele dever ser alphanumerico mesmo, devido vc querer zeros a esquerda do codigo
e não pode ser primario,
{crie um campo primario e autoincremento em sua tabela como já citei, e pronto}
seu sistema funcionará como o do outro programador.

bém como vc mudou o campo para alphanumerico, refaça dele ele no fields da taabela
e faça um novo add. dele, para que sue sistema atulize ele para alphanumerico.

campo autoicremento, é incrementado automaticamente pela tabela
ao receber gravação de um registro, é automatico, se deixar assim
vc Não poderá usar o codigo que te passei.
e ele sendo primario, não aceita duplicidade.
vc pode até usar como autoincremento desde que não seja primario, lembrando
que assim o codigo que te passei não pode ser usado.
GOSTEI 0
Carlos Magno

Carlos Magno

26/11/2012

Antes de tentar seu post só queria fazer mais 3 perguntas:

1 - Isso vai resolver o problema de KEY VIOLATION?

2 - Posso apenas adicionar um campo na tabela para o autoincremento e coloca-lo como primário?

3 - Caso não, como passar os dados já existentes na tabela para a nova tabela criada?
GOSTEI 0
POSTAR