thalesoli 0 Denunciar post Postado Junho 14, 2011 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
Chrnos 30 Denunciar post Postado Junho 21, 2011 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
thalesoli 0 Denunciar post Postado Junho 21, 2011 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
Chrnos 30 Denunciar post Postado Junho 21, 2011 Como está o código onde o erro ocorre? Pode copiar ele aqui? Compartilhar este post Link para o post Compartilhar em outros sites
thalesoli 0 Denunciar post Postado Junho 21, 2011 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
Chrnos 30 Denunciar post Postado Junho 21, 2011 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
thalesoli 0 Denunciar post Postado Junho 21, 2011 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
Chrnos 30 Denunciar post Postado Junho 21, 2011 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
thalesoli 0 Denunciar post Postado Junho 21, 2011 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
Chrnos 30 Denunciar post Postado Junho 22, 2011 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
thalesoli 0 Denunciar post Postado Junho 22, 2011 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
Chrnos 30 Denunciar post Postado Junho 22, 2011 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
thalesoli 0 Denunciar post Postado Junho 22, 2011 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
wllf 2 Denunciar post Postado Junho 24, 2011 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
thalesoli 0 Denunciar post Postado Junho 24, 2011 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
wllf 2 Denunciar post Postado Junho 24, 2011 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