Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

thalesoli

Record not found or changed by another user + campo Serial

Recommended Posts

Boa tarde a todos,

 

Sei que este tópico pode ser duplicado mas já procurei e não encontrei a resposta.

 

Utilizo o Delphi XE com DBX e MS SQL 2008 R2.

 

No banco, as tabelas do sistema tem campos seriais, ou sequenciais. Por essa eu configurei nos providerflags dos campos chaves a seguinte maneira: pfInWhere, pfInKey. Eu removi o pfInUpdate, pois o campo não poderá ser inserido e nem atualizado por ser serial. A propriedade UpdateMode está em upWhereKeyOnly.

 

Quando faço o applyupdates após um insert, o erro de "Record not found or changed by another user" aparece. Já revisei diversas vezes os providers e também verifiquei se na tabela em questão não é alterado os campos em algum trigger (que não é).

 

Alguém já passou por essa situação?

 

desde já agradeço a todos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Você usa transações? Está comitando o processo antes do applyupdate? Como está o código onde dá o erro?

 

[ ]'s

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Você usa transações? Está comitando o processo antes do applyupdate? Como está o código onde dá o erro?

 

[ ]'s

 

 

Obrigado pelo interesse ao post

 

Eu não estou controlando transações manualmente não.

 

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Como está o código onde o erro ocorre? Pode copiar ele aqui?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Como está o código onde o erro ocorre? Pode copiar ele aqui?

 

O sql é esse:

 

 


SELECT Codigo, TagDB, Tipo, Razao, Fantasia, Regiao, Setor, 
             Categoria_Grupo, Categoria_SubGrup, Status, Senha, 
             Cadastro, UAlteracao, Cancelamento, SenhaUra, Flag01, 
             Flag02, Flag03, Flag04, Flag05, Flag06, CodigoUra, SenhaACSP,
             Flag07, Flag08,Flag09
FROM ASSOCIADOS


 

Os campos Código e TagDB estão marcados com o provider flags pfInWhere, pfInUpdate, pfInKey e os demais somente com os dois primeiros (no TSQLQuery).

 


object qryAssociados: TSQLQuery
 MaxBlobSize = -1
 Params = <>
 SQL.Strings = (
   'SELECT Codigo, TagDB, Tipo, Razao, Fantasia, Regiao, Setor, '

     '              Categoria_Grupo, Categoria_SubGrup, Status, Senha,' +
     ' '

     '              Cadastro, UAlteracao, Cancelamento, SenhaUra, Flag' +
     '01, '

     '              Flag02, Flag03, Flag04, Flag05, Flag06, CodigoUra,' +
     ' SenhaACSP,'
   '              Flag07, Flag08,Flag09'
   'FROM ASSOCIADOS')
 SQLConnection = dmdConexao.conConexao
 Left = 24
 Top = 8
 object qryAssociadosCodigo: TIntegerField
   FieldName = 'Codigo'
   ProviderFlags = [pfInUpdate, pfInWhere, pfInKey]
   Required = True
 end
 object qryAssociadosTagDB: TStringField
   FieldName = 'TagDB'
   ProviderFlags = [pfInUpdate, pfInWhere, pfInKey]
   Required = True
   FixedChar = True
   Size = 1
 end
 object qryAssociadosTipo: TStringField
   FieldName = 'Tipo'
   Size = 25
 end
 object qryAssociadosRazao: TStringField
   FieldName = 'Razao'
   Size = 60
 end
 object qryAssociadosFantasia: TStringField
   FieldName = 'Fantasia'
   Size = 60
 end
 object qryAssociadosRegiao: TStringField
   FieldName = 'Regiao'
   Size = 50
 end
 object qryAssociadosSetor: TStringField
   FieldName = 'Setor'
   Size = 50
 end
 object qryAssociadosCategoria_Grupo: TStringField
   FieldName = 'Categoria_Grupo'
   Size = 50
 end
 object qryAssociadosCategoria_SubGrup: TStringField
   FieldName = 'Categoria_SubGrup'
   Size = 50
 end
 object qryAssociadosStatus: TIntegerField
   FieldName = 'Status'
 end
 object qryAssociadosSenha: TStringField
   FieldName = 'Senha'
   Size = 10
 end
 object qryAssociadosCadastro: TSQLTimeStampField
   FieldName = 'Cadastro'
 end
 object qryAssociadosUAlteracao: TSQLTimeStampField
   FieldName = 'UAlteracao'
 end
 object qryAssociadosCancelamento: TSQLTimeStampField
   FieldName = 'Cancelamento'
 end
 object qryAssociadosSenhaUra: TStringField
   FieldName = 'SenhaUra'
   Size = 10
 end
 object qryAssociadosFlag01: TStringField
   FieldName = 'Flag01'
   FixedChar = True
   Size = 5
 end
 object qryAssociadosFlag02: TStringField
   FieldName = 'Flag02'
   FixedChar = True
   Size = 5
 end
 object qryAssociadosFlag03: TStringField
   FieldName = 'Flag03'
   FixedChar = True
   Size = 5
 end
 object qryAssociadosFlag04: TStringField
   FieldName = 'Flag04'
   FixedChar = True
   Size = 5
 end
 object qryAssociadosFlag05: TStringField
   FieldName = 'Flag05'
   FixedChar = True
   Size = 5
 end
 object qryAssociadosFlag06: TStringField
   FieldName = 'Flag06'
   FixedChar = True
   Size = 5
 end
 object qryAssociadosCodigoUra: TStringField
   FieldName = 'CodigoUra'
   Size = 10
 end
 object qryAssociadosSenhaACSP: TStringField
   FieldName = 'SenhaACSP'
   Size = 8
 end
 object qryAssociadosFlag07: TStringField
   FieldName = 'Flag07'
   FixedChar = True
   Size = 5
 end
 object qryAssociadosFlag08: TStringField
   FieldName = 'Flag08'
   FixedChar = True
   Size = 5
 end
 object qryAssociadosFlag09: TStringField
   FieldName = 'Flag09'
   FixedChar = True
   Size = 5
 end
end

 

O Provider está com a configuração:

 


object dspAssociados: TDataSetProvider
 DataSet = qryAssociados
 UpdateMode = upWhereKeyOnly
 OnUpdateError = dspAssociadosUpdateError
 BeforeUpdateRecord = dspPessoasBeforeUpdateRecord
 OnGetTableName = dspAssociadosGetTableName
 Left = 24
 Top = 56
end

 

o evento OnUpdateError tem o código

 

Response := rrSkip;

 

E o client está somente ligado ao provider.

 

No cliente tem o evento afterpost onde:

 


var
 evento : TDataSetNotifyEvent;
begin
 evento := DataSet.AfterPost;
 DataSet.AfterPost := nil;
 try
   if TClientDataSet(DataSet).ApplyUpdates(0) <> 0 then
     raise Exception.Create('Erro: ' + FUltimoErro);
   if DataSet = cdsAssociados then
     begin
       if not cdsFinanceiro.Active then
         cdsFinanceiro.Open;
       if cdsFinanceiro.IsEmpty then
         Exit;
       cdsFinanceiro.Edit;
       cdsFinanceiroDestinatario.AsString := cdsAssociadosRazao.AsString;
       cdsFinanceiro.Post;
     end;
 finally
   DataSet.AfterPost := evento;
 end;

 

No evento do clientdataset OnReconsileError tem o código

 


 Action := raSkip;
 FUltimoErro := E.Message;

 

Ai no applyupdates da o erro.

 

 

falow

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Algumas questões que me vem a cabeça por hora, já que não tenho como validar seu código no momento....

 

1. Todos os campos obrigatórios da tabela estão preenchidos corretamente antes do post?

2. O campo chave é um autoincremento? Li alguma coisa a algum tempo a respeito de problemas com esse comando com campos assim... se eu tiver tempo vou tentar recuperar o material na net.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Algumas questões que me vem a cabeça por hora, já que não tenho como validar seu código no momento....

 

1. Todos os campos obrigatórios da tabela estão preenchidos corretamente antes do post?

2. O campo chave é um autoincremento? Li alguma coisa a algum tempo a respeito de problemas com esse comando com campos assim... se eu tiver tempo vou tentar recuperar o material na net.

 

1 - Sim todos os campos estão preenchidos

2 - Neste caso não tem campos autoinc. Em outra tabela eu recupero e coloco após o applyupdates.

O que eu acho mais estranho é que foi de uma hora para outra que começo a dar erro e não é em todos as querys do datamodulo (pois no datamodulo tem alguns cadastros que estão juntos).

Compartilhar este post


Link para o post
Compartilhar em outros sites

Será que o fato de você usar no evento OnUpdateError o código

Response := rrSkip;

 

Não pode estar mascarando o real erro que ocorre no seu sistema? Não usei ainda esta versão do delphi, mas já tive problemas nas versões mais antigas por ter um tratamento de erros que mascarava o erro real com uma mensagem genérica ou sem retornar mensagem alguma (try except end da vida)...

 

É difícil opinar sem poder acompanhar todo o fluxo....

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Será que o fato de você usar no evento OnUpdateError o código

Response := rrSkip;

 

Não pode estar mascarando o real erro que ocorre no seu sistema? Não usei ainda esta versão do delphi, mas já tive problemas nas versões mais antigas por ter um tratamento de erros que mascarava o erro real com uma mensagem genérica ou sem retornar mensagem alguma (try except end da vida)...

 

É difícil opinar sem poder acompanhar todo o fluxo....

 

 

Você utiliza qual resposta?

 

Eu uso Skip, pois ele não para a atualização do registro que deu erro e deixa na cache as alterações como não aplicadas.

 

http://docwiki.embarcadero.com/VCL/en/Provider.TResolverResponse

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu geralmente utilizo stored procedures nos meus projetos thalesoli... simplesmente chamo a procedure e deixo para o BD a responsabilidade de salvar e retornar os possíveis erros para a aplicação no retorno da procedure. Quando comecei a trabalhar com delphi, eu utilizava as querys para inserts, updates, deletes e consultas, montando a sql em runtime e dando o comando para abrir e/ou executar o comando dentro de um try except, sendo que no except eu colocava o tratamento para recuperar o erro do banco e mostrava para o usuário uma mensagem padrão no showmessagem de minha escolha, trazendo ao final o erro retornado.

 

Era algo +/- assim que eu costumava usar...

 

 try 
    //seu codigo 
 except on e : exception do 
    showmessage('Ocorreu o seguinte erro: '+ e.message); 
 end

 

Fazendo o insert direto pelo banco via procedures eu não precisei mais me preocupar em ficar atualizando componentes na tela... e fazendo consultas via SQL eu não tinha perda de performance, desde que eu estruturasse as tabelas corretamente e usasse bons índices. Vou ver se no feriado consigo pesquisar algo sobre o seu problema e ver se há alguma dica relevante, mas não prometo nada.

 

[ ] 's

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu geralmente utilizo stored procedures nos meus projetos thalesoli... simplesmente chamo a procedure e deixo para o BD a responsabilidade de salvar e retornar os possíveis erros para a aplicação no retorno da procedure. Quando comecei a trabalhar com delphi, eu utilizava as querys para inserts, updates, deletes e consultas, montando a sql em runtime e dando o comando para abrir e/ou executar o comando dentro de um try except, sendo que no except eu colocava o tratamento para recuperar o erro do banco e mostrava para o usuário uma mensagem padrão no showmessagem de minha escolha, trazendo ao final o erro retornado.

 

Era algo +/- assim que eu costumava usar...

 

 try 
	//seu codigo 
 except on e : exception do 
	showmessage('Ocorreu o seguinte erro: '+ e.message); 
 end

 

Fazendo o insert direto pelo banco via procedures eu não precisei mais me preocupar em ficar atualizando componentes na tela... e fazendo consultas via SQL eu não tinha perda de performance, desde que eu estruturasse as tabelas corretamente e usasse bons índices. Vou ver se no feriado consigo pesquisar algo sobre o seu problema e ver se há alguma dica relevante, mas não prometo nada.

 

[ ] 's

 

Eu não curto utilizar stored procedures, pois fica preso ao banco e também no caso o sistema já existe, eu estou apenas tentando migrar de BDE para DBX.

Não adianta colocar try / except em applyupdates, pois este método infelizmente encapsula as exceptions retornando somente a quantidade de erros que teve durante o processo.

A única coisa que me intriga é que enquando eu migrava, este cds que está dando erro, estava funcionando, depois de fazer todos os cds que são necessários na tela que gerou o problema. Já pensei que era questão de ordenação das units mas não é o problema.

A única solução que encontrei até agora foi desenvolver uma rotina para eu mesmo fazer o applyupdates:

 

type necessário
 TSQLParam = record
nome: string;
valor: Variant;
tipo: TFieldType;
 end;

procedure TdmdConexao.OnUpdateRecord(Sender: TObject; SourceDS: TDataSet;
 DeltaDS: TCustomClientDataSet; UpdateKind: TUpdateKind; var Applied: Boolean);
var
 Sql, insertParam: TStringList;
 i: Integer;
 tableName: WideString;
 addJuncao: Boolean;
 Params: TList<TSQLParam>;
begin
 //Esta procedure tem a função de gerar os sqls de insert, update ou delete do
 //applyupdates

 //inicio os objetos
 Sql := TStringList.Create;
 insertParam := TStringList.Create;
 Params := TList<TSQLParam>.Create;
 try
//é necessário que o provider tem o evento OnGetTableName configurado
TDataSetProvider(Sender).OnGetTableName(Sender, SourceDS, tableName);
addJuncao := False;

//determino o modelo de sql
case UpdateKind of
 	ukModify:
   	begin
     	//sql de update
     	Sql.Add('update ' + tableName + ' set ');
     	//inicialmente vou no último registro do delta para obter os novos valores
     	DeltaDS.Last;
     	//adiciono todos os campos que sofreram alterações e que não sejam chave
     	for i := 0 to SourceDS.Fields.Count - 1 do
       	begin
         	if (pfInUpdate in SourceDS.Fields[i].ProviderFlags) and
        		not (pfInKey in SourceDS.Fields[i].ProviderFlags) and
        		not VarIsEmpty(DeltaDS.FieldByName(SourceDS.Fields[i].FieldName).NewValue) then
           	begin
             	if addJuncao then
               	Sql.Add(',');
             	addJuncao := True;
             	Sql.Add(SourceDS.Fields[i].FieldName + ' = :' +
               	SourceDS.Fields[i].FieldName);
             	// adiciono o parametro
             	Params.Add(createSQLParam(SourceDS.Fields[i].FieldName,
               	DeltaDS.FieldByName(SourceDS.Fields[i].FieldName).Value,
               	SourceDS.Fields[i].DataType));
           	end;
       	end;
     	//vou para o primeiro registro para que possa pegar o valor dos campos chaves
     	DeltaDS.First;
     	//adiciono a condição de update
     	Sql.Add('where ');
     	addJuncao := False;
     	for i := 0 to SourceDS.Fields.Count - 1 do
       	begin
         	if (pfInKey in SourceDS.Fields[i].ProviderFlags) then
           	begin
             	if addJuncao then
               	Sql.Add(' and ');
             	Sql.Add(' ' + SourceDS.Fields[i].FieldName + ' = :' +
               	SourceDS.Fields[i].FieldName);
             	addJuncao := True;
             	// adiciono o parametro
             	Params.Add(createSQLParam(SourceDS.Fields[i].FieldName,
               	DeltaDS.FieldByName(SourceDS.Fields[i].FieldName).Value,
               	SourceDS.Fields[i].DataType));
           	end;
       	end;
   	end;
 	ukInsert:
   	begin
     	//gero o comando de insert
     	Sql.Add('INSERT INTO ' + tableName + '(');
     	//adiciono todos os campos que não estiverem vazios e que estiverem com pfInUpdate
     	for i := 0 to SourceDS.Fields.Count - 1 do
       	begin
         	if (pfInUpdate in SourceDS.Fields[i].ProviderFlags) and
        		not VarIsEmpty(DeltaDS.FieldByName(SourceDS.Fields[i].FieldName).NewValue) then
           	begin
             	if addJuncao then
               	begin
                 	Sql.Add(', ');
                 	insertParam.Add(', ');
               	end;
             	Sql.Add(SourceDS.Fields[i].FieldName);
             	insertParam.Add(':' + SourceDS.Fields[i].FieldName);
             	addJuncao := True;
             	// adiciono o parametro
             	Params.Add(createSQLParam(SourceDS.Fields[i].FieldName,
               	DeltaDS.FieldByName(SourceDS.Fields[i].FieldName).Value,
               	SourceDS.Fields[i].DataType));
           	end;
       	end;
     	Sql.Add(') values (' + insertParam.Text + ')');
   	end;
 	ukDelete:
   	begin
     	//monto o comando de delete
     	Sql.Add('delete from ' + tableName + ' where ');
     	//listando todos os campos chaves no where
     	for i := 0 to SourceDS.Fields.Count - 1 do
       	begin
         	if pfInKey in SourceDS.Fields[i].ProviderFlags then
           	begin
             	if addJuncao then
               	Sql.Add(' and ');
             	Sql.Add(' ' + SourceDS.Fields[i].FieldName + ' = :' +
               	SourceDS.Fields[i].FieldName);
             	addJuncao := True;
             	// adiciono o parametro
             	Params.Add(createSQLParam(SourceDS.Fields[i].FieldName,
               	DeltaDS.FieldByName(SourceDS.Fields[i].FieldName).Value,
               	SourceDS.Fields[i].DataType));
           	end;
       	end;
   	end;
end;
//executo o comando sql com os parametros necessários
executeSQL(Sql.Text, Params);
//se nenhum erro aconecer, será marcado como aplicado o comando
Applied := True;
 finally
Sql.Free;
insertParam.Free;
Params.Free;
 end;
end;

 

só que é difícil.. o clientdataset é muito instável, vira e mexe da algum tipo de erro. Já tive diversos problemas com ele. E olha que estou utilizando o Delphi Xe e nele ainda está tendo problema.

 

se tiver alguma idéia me fala

 

um abraço

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu prefiro deixar o programa como uma "casca", deixando o BD fazer o seu trabalho.... muitas regras de negócio as vezes causam uma série de acessos ao banco para validações que numa procedure poderiam estar sendo feitas na mesma chamada... outras vezes um relatório pode ter um tempo de processamento muito grande que pode locar as tabelas para outras operações, dando problemas em rotinas que trabalham em rede com um grande número de usuários ativos... sem contar que se você for migrar um programa de um banco para outro e a sintaxe do sql mudar te obrigaria a ter de rever todo o programa e a ter dois programas paralelos que fazem a mesma coisa mas são diferentes só pela parte do SQL, dando trabalho dobrado na manutenção para correção de eventuais bugs (o que seria mais fácil de se contornar adaptando-se o script das procedures afetadas ao invés do executável ao meu ver)... sem contar que executar comandos direto no banco via procedure com tabelas bem planejadas e com bons índices é infinitamente mais rápido que executar via programa até onde meu parco conhecimento me diz....

 

Mas é fato que, dependendo do tamanho da aplicação, isso tudo passa despercebido e a forma de programar sempre fica a critério de quem desenvolve o programa.... pra mim ao menos, pela experiência que adquiri ao longo destes anos como programador em áreas diversas e com empresas de porte diversos, ainda fico com as procedures...

 

[]'s

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu prefiro deixar o programa como uma "casca", deixando o BD fazer o seu trabalho.... muitas regras de negócio as vezes causam uma série de acessos ao banco para validações que numa procedure poderiam estar sendo feitas na mesma chamada... outras vezes um relatório pode ter um tempo de processamento muito grande que pode locar as tabelas para outras operações, dando problemas em rotinas que trabalham em rede com um grande número de usuários ativos... sem contar que se você for migrar um programa de um banco para outro e a sintaxe do sql mudar te obrigaria a ter de rever todo o programa e a ter dois programas paralelos que fazem a mesma coisa mas são diferentes só pela parte do SQL, dando trabalho dobrado na manutenção para correção de eventuais bugs (o que seria mais fácil de se contornar adaptando-se o script das procedures afetadas ao invés do executável ao meu ver)... sem contar que executar comandos direto no banco via procedure com tabelas bem planejadas e com bons índices é infinitamente mais rápido que executar via programa até onde meu parco conhecimento me diz....

 

Mas é fato que, dependendo do tamanho da aplicação, isso tudo passa despercebido e a forma de programar sempre fica a critério de quem desenvolve o programa.... pra mim ao menos, pela experiência que adquiri ao longo destes anos como programador em áreas diversas e com empresas de porte diversos, ainda fico com as procedures...

 

[]'s

 

Trabalhei por muito tempo numa empresa que o sistema inteiro era em stored procedures.. e eles queriam troca de banco.. não vão conseguir não. Pois além das sintaxes serem diferentes, os componentes de acesso são também.

Eu prefiro trabalhar OO com n-tier, não necessariamente com datasnap que é a tecnologia da embarcadeiro para isso. Depois que eu estudei outras linguagens, principalmente Java, trabalhar de mandeira estruturada não é tão bom assim. Hoje eu tento manter as coisas em padrões de projeto OO, como MVC (model view controller).

 

Mas de qualquer jeito isso tudo não resolve o meu problema, pois existem diversas maneiras de desenvolver e nenhum é perfeita.

 

Se alguém tiver alguma idéia é só falar.

 

um abraço

Compartilhar este post


Link para o post
Compartilhar em outros sites

Colega tive um problema parecido à algum tempo atrás, resolvi assim, o campos Chave primária:

provider flags pfInWhere, pfInUpdate, pfInKey

 

o restante dos campos:

provider flags pfInUpdate

 

Só que para chegar a essa solução demorei alguns dias.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Colega tive um problema parecido à algum tempo atrás, resolvi assim, o campos Chave primária:

provider flags pfInWhere, pfInUpdate, pfInKey

 

o restante dos campos:

provider flags pfInUpdate

 

Só que para chegar a essa solução demorei alguns dias.

 

Opa valeu tentar me ajudar, mas infelizmente também não funcionou, removi todos os providers flags dos demais campos deixando somente o pfInUpdate e nos campos chave deixei os demais e não funcionou. Da o mesmo erro.

 

é difícil....

 

valeu

Compartilhar este post


Link para o post
Compartilhar em outros sites

Colega não esquenta, você vai achar a solução ou ak ou em outro lugar, o gostoso da programação é isso, as vezes uma vírgula muda tudo,

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.