dbExpress Interbase 6.5 Delphi 7.0

Delphi

17/03/2006

Esta semana o computador me pegou para dar uma coça... Mas fo a semana toda e no fim ainda esta me dando outra.. mMe segurem senão vou quebrar esta joça

Uso os componetes
SQLConnection1 -> SQLDataSet1 -> DataSetProvider1 -> ClientDataSet1 -> DataSource1

ligados conforme manda as boas normas

Tenho tb um DbGrid , um Data Sorce e um Botao

Apresnto os dados faço algumas alteraçoes e quando clico no botão

procedure TForm1.Button1Click(Sender: TObject);
begin
ClientDataSet1.ApplyUpdates(0);
end;

[b:09ef8812a7]me dá o erro abaixo :[/b:09ef8812a7]


[URL=http://imageshack.us][img:09ef8812a7]http://img85.imageshack.us/img85/6292/imagemdbespress19wc.png[/img:09ef8812a7][/URL]


[b:09ef8812a7]e seguindo com a execução do aplicativo , me da o erro[/b:09ef8812a7]


[URL=http://imageshack.us][img:09ef8812a7]http://img116.imageshack.us/img116/8593/imagemdbespress21lo.png[/img:09ef8812a7][/URL]

o problema é que qualquer tabela que vou abrir no Interbase , e depois tento salvar alguns dados <usando a tecnologia DbExpress> , me da este erro....

Usando o IBExpert consigo salvar normalmente as modificaçoes...

Alem disso ja desistalei o Delphi 7.0 e tb o Interbase 6.5 ... E nada

espero muito ajuda . Obtrigado...


Marco Salles

Marco Salles

Curtidas 0

Respostas

Vinicius2k

Vinicius2k

17/03/2006

Marco,

Verifique a instrução SQL do TSQLDataSet.

Com dbExpress, os nomes dos objetos do banco de dados (tabelas, campos, SPs, etc) devem estar em maiúsculas. Ex:
// erro
select campo1, campo2, campo3 from tabela
// correto
select CAMPO1, CAMPO2, CAMPO3 from TABELA



GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

vinicius2k , é um dos exemplos mais bobo que eu ja fiz na vida

é um exemplo do livro do Guinther , da pag14 do seu livro (Programação para banco de dados)

Para se ter uma idéia da simplicidade o SQL esta Assim

SELECT *FROM CUSTOMER


é o exemplo dele do próprio guinther

isto esta me dando danda dor de cabeça , que ja desistalei e reistalei tudo que tinha direito na máquina... E O pior , tinha componentes de terceiros que vou ter o trabalhão de reistala-los

Levei o problema para casa e la tb deu o mesmo erro

o erro acontece , quando eu simplesmente edito qualquer campo e dou um ClientDataSet1.ApplyUpdates(*); *=0 ou -1

verifiquei devido a segunda mensagem que Fala de Chave Primária.. Algo esta reclamando da ausencia desta Chave.. Mas em processo de edição , não teria sentido ele reclamar Ou teria ????

Outra coisa , criei uma Banco de Dados no Interbase , e nele uma tabela muito simples com dois campos apenas

A Dll desta tabela e esta assim

CREATE TABLE TOTENTANDO (
    COD_VENDA   INTEGER NOT NULL,
    NOME_ALUNO  VARCHAR(30)
);


eu então atraves do DbGrid insiro os dados , tomando o cuidado para não ter nenhum registro repetido e logo no primeiro dado quando clico no botão ele da a mensagem de erro escrita la em cima no Primeiro post..

Porem , quando se tem um Banco com Generator e Triger (Que é o caso do CUSTOMER) eu consegui inserir e entrar com os Dados , so que tive que excluir o Campo ´PK´ do Sql , deixando o Banco retornar o Valor para esta Chave...

[b:9cfb56af5d]Mas qy=uando não se tem Generator e ne Triger , como no caso da tabela que criei la em cima , porque eu não esto uconseguindo digitar dados , Mesmo quando insiro todos os valores
corretamente para os Campos (Cod_Venda e Nome_Aluno)[/b:9cfb56af5d]

O Sql neste ultimo Caso tb é Muito simples

SELECT *FROM TOTENTANDO 


Onde TOTENTANDO É O nome do minha tabela

[b:9cfb56af5d]Conclusão : So posso incluir algum valor se a chave primaria tiver um Generator.. Não tem sentido isso.. MAs é assim que esta funcionando comigo... As tabelas que eu tenho Chave Primaria com GENERATOR eu uso a query sem definir esse campo e incluo os outros dados no DBGRID , ai não da erro... Mas isto não é muito lógico ou é [/b:9cfb56af5d]


GOSTEI 0
Vinicius2k

Vinicius2k

17/03/2006

Marco,

O exemplo que você está seguindo pede que você altere os valores padrão de alguma das propriedades do TDataSetProvider?
Se sim, quais?

Não creio que seja um problema relacionado a existir ou não Generator + Trigger e sim existir ou não uma chave primária:

Se sua tabela não tem uma PK e o [b:1d1b6c2286]UpdateMode[/b:1d1b6c2286] do TDataSetProvieder estiver como [b:1d1b6c2286]upWhereKeyOnly[/b:1d1b6c2286], ele não possui uma chave para utilizar no Where, logo, não consegue atualizar...

Não posso afirmar que seja isso... É só um exemplo de como as configurações do TDataSetProvider influenciam no tratamento da informação.


GOSTEI 0
Adriano Santos

Adriano Santos

17/03/2006

Marco, blz?

Cara estou com o Vinicius2K, acredito que não tenha nada a ver com o fato de ter ou não PK na tabela. Acredito que seja alguma configuração de propriedade mesmo.

Vc tem como me passar o exemplinho que está tendo dificuldade? Queria testar este final de semana. Não sou nenhum expert em DBExpress, mas talvez consiga algo.

flw.


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

Vc tem como me passar o exemplinho que está tendo dificuldade? Queria testar este final de semana.



Então vamos lá

A estrutura que estou usando [color=darkblue:aed0e1e635][b:aed0e1e635]Poxa , acho que todos que puderem testar e comentar o seu resultado , seria otimo..

O Exemplinho é muito fácil e rápido

Não toma nen um minuto.. Mas me ajuda demais[/b:aed0e1e635][/color:aed0e1e635]

A estrutura que to usando é


SQLConnection1 -> Clico no SqlConnection e escolho Interbase me conecto ao banco Employye... Todos tem este banco de exemplo no Delphi7.0 ; Que fica em C:\Arquivo de programas\Programas comuns\Borland Shared\Data\Employee SQLQuery a)Propriedade SqlConnection atribuo Sqlconnection1 b)Propriedade Sql digito SELECT *FROM CUSTOMER DatasetProvinder a)Propriedade ProvinderName atribuo o SqlQuery ClintDataSet a)Propriedade ProvinderName Atribuo DataSetProvinder1 DataSource a)Propriedade DataSet atribuo ClientDateSet1 DBgrid Ligo ao Datasource1 DbNavegator Lido ao DataSouce1 Botão 1 no evento OnClick Digito ClientDataSet1.ApplyUpdates(0);


[color=darkblue:aed0e1e635][b:aed0e1e635]Ta ai ... Rodo a aplicação e façao aqualquer alteração no DbGrid e Clico no Botão... [/b:aed0e1e635][/color:aed0e1e635]



:?: :?: :?: :?: :?: :?: :?:
Faço simplesmente isso e não da certo :cry: :cry: :cry: O que acontece no caso de voce :?: :?: :?:


Por favor , postem os resultados.....


GOSTEI 0
Vinicius2k

Vinicius2k

17/03/2006

Marco,

Só para me certificar que foi apenas um erro de digitação...
A instrução SQL que você informa está incorreta: falta um espaço entre o ´*´ e o ´FROM´.


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

Só para me certificar que foi apenas um erro de digitação... A instrução SQL que você informa está incorreta: falta um espaço entre o ´*´ e o ´FROM´.


Não , foi erro mesmo .... Não me atinei para isto.. Nunca , nen na epoca do BDE

Parece que esta resolsolvido o problema.... Mas porque da esta diferença tão grande ????Nunca me atinei isto , nen usando o BDE , nunca deu falta desse Espaço .... Agora eu ja não sei se eu colocava ou colcava naturalmente , mas usando BDE isto para mim nunca foi problema


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

Vincius2k brilhantemente de maneira sutil voce tirou um elefante em cima das minhas costas. Mas tem outro montado

Sobre esse asunto :

Se sua tabela não tem uma PK e o UpdateMode do TDataSetProvieder estiver como upWhereKeyOnly, ele não possui uma chave para utilizar no Where, logo, não consegue atualizar...


Nomeu caso que estou estudando a[b:464ffd2c75] tabela employye [/b:464ffd2c75]temos uma chave cujo select esta assim:
Select * From CUSTOMER


a biografia manda , manda efetuar os seguintes passos

1)No ClinteDataset de um AddFields 2)selecione todos os campos com exceção do Chave(Que no caso é o CUST_NO) 3)altere na propriedade ProvinderFlags , opção pfInWhere para false exemplo:Ficando esse Tfields assim : ProviderFlags = [pfInUpdate] 4)Para o Campo Chave , altere a Propriedade ProvinderFlags a Opção exemplo: Ficanodo TFiled para a chave assim: ProviderFlags = [pfInUpdate, pfInWhere, pfInKey] 5)Finalmente va em UpdateMode do TDataSetProvieder e altere para upWhereKeyOnly, ficando assim :UpdateMode = upWhereKeyOnly


Com essas medidas , que estão descritas no livro do Guinther pag 15
estamos fazendo uma configuração na clausula WHERE , para ser otimizada.... Isto é importante para processar a instrução de maneira muito mais rápida

:cry: :cry: :cry: :cry:
[color=darkblue:464ffd2c75][b:464ffd2c75]Na hora de executar a aplicação , da erro.. Não consigo atualizar (ClintDataSet.ApplyUpdates(0));[/b:464ffd2c75][/color:464ffd2c75]

[b:464ffd2c75]Alguem ou o proprio Vinicus , consegue enxergar algo nesse processo que não estou fazendo ou deixando de Fazer...[/b:464ffd2c75]


[b:464ffd2c75]O Elefante esta pesado[/b:464ffd2c75]

Se quiserem testar , é importante porque esta tecnologia é a que estará em ´Alfa´ durante os proximos cinco anos.. Temos que começar

Muito obrigado


GOSTEI 0
Martins

Martins

17/03/2006

Olá [b:85fcc4154e]Marco Salles[/b:85fcc4154e], fiz tudo q está sendo indicado por vc, mudei
1)No ClinteDataset de um AddFields 2)selecione todos os campos com exceção do Chave(Que no caso é o CUST_NO) 3)altere na propriedade ProvinderFlags , opção pfInWhere para false exemplo:Ficando esse Tfields assim : ProviderFlags = [pfInUpdate] 4)Para o Campo Chave , altere a Propriedade ProvinderFlags a Opção exemplo: Ficanodo TFiled para a chave assim: ProviderFlags = [pfInUpdate, pfInWhere, pfInKey] 5)Finalmente va em UpdateMode do TDataSetProvieder e altere para upWhereKeyOnly, ficando assim :UpdateMode = upWhereKeyOnly


E testei, deu tudo certo aqui, sem mensagem de erro nem nada, quer q eu mande o exemplo para vc ver? Se quiser é só me passar o e-mail, não disponibilizo para mais pessoas pq não tenho um server para hospedar o código.

Abraço!


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

Martins , acho que voce deve ter conseguido .. MAs não desta forma .. Pois to apostando que ela contem um erro

Se não corrigir este erro , futuramente outras pessaos irão errar também

Eu tenho 99¬ de certeza que esta errado isto aqui

1)No [b:800e7c8393]ClinteDataset[/b:800e7c8393] de um AddFields 2)selecione todos os campos com exceção do Chave(


Não é no ClinteDataset que se deve alterar os Flags mas sim No SqlDataSet.. Pois são eles os TSql.... os Responsáveis por passaram Uma instrução Slq para o DatasetProvider , para que este execute a instrução no Banco..


[b:800e7c8393]Voce pode ter os mesmo fields no ClinteDataSert , mas é necessário que se tenha esses Tfilds no SqlQuery.. Pois bem , esta fo ia conclusão que eu cheguei lendo pesquisando e experimentando[/b:800e7c8393]


GOSTEI 0
Martins

Martins

17/03/2006

Marcos, segui todos esses passos.

1)No ClinteDataset de um AddFields 2)selecione todos os campos com exceção do Chave(Que no caso é o CUST_NO) 3)altere na propriedade ProvinderFlags , opção pfInWhere para false exemplo:Ficando esse Tfields assim : ProviderFlags = [pfInUpdate] 4)Para o Campo Chave , altere a Propriedade ProvinderFlags a Opção exemplo: Ficanodo TFiled para a chave assim: ProviderFlags = [pfInUpdate, pfInWhere, pfInKey] 5)Finalmente va em UpdateMode do TDataSetProvieder e altere para upWhereKeyOnly, ficando assim :UpdateMode = upWhereKeyOnly


E não tive problemas, vc quer q eu te mande o exemplo para vc dá uma olhada, ressalto ainda q o mesmo foi feito em Delphi 6, q é a única versão q tenho nessa máquina.

Valew!!!!


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

é martins .. Isto me encabula..

Porque teoricamente quem ´´e responsável por enviar as instruçoes Sqk para o DataSetProvider é nesse caso O SqlQuery e não o clientDataSet

Se voce consegui realizar isto somente definindo Tfields para o ClinteDataSet Meu email ´´e este :

salhamoda@uol.com.br


GOSTEI 0
Martins

Martins

17/03/2006

[quote:d0b4b188fb=´Marco Salles´]é martins .. Isto me encabula..

Porque teoricamente quem ´´e responsável por enviar as instruçoes Sqk para o DataSetProvider é nesse caso O SqlQuery e não o clientDataSet

Se voce consegui realizar isto somente definindo Tfields para o ClinteDataSet Meu email ´´e este :

salhamoda@uol.com.br[/quote:d0b4b188fb]

Enviei para seu e-mail, e confirmei aquele ant-spam chato da UOL, hehehe :P

Tá, qualquer coisa, posta aqui.

Valew


GOSTEI 0
Vinicius2k

Vinicius2k

17/03/2006

Marco,

No modelo proposto, ´quem manda´ nas configurações de TFields é o DataSet (TSQLDataSet/TSQLQuery) e não o TClientDataSet. [b:1e298542e1]As alterações na propriedade ProviderFlags devem ser feitas nos TFields do TSQLQuery/TSQLDataSet[/b:1e298542e1] e você nem precisa de TFields no TClientDataSet para isso (logicamente pode precisar para outras coisas).

1. Adicione todos os TFields no TSQLQuery/TSQLDataSet.
2. Faça as mudanças necessárias ao seu propósito.
3. Então, se quiser, adicione os TFields no TClientDataSet. Você verá que os TFields do TClientDataSet terão herdadas as alterações feitas nos TFields do TSQLDataSet/TSQLQuery.

Correndo o risco de parecer pretencioso por se tratar de um livro do Guinther, se ele não o orienta desta forma, infelizmente, ele está errado.


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

No modelo proposto, ´quem manda´ nas configurações de TFields é o DataSet (TSQLDataSet/TSQLQuery) e não o TClientDataSet. As alterações na propriedade ProviderFlags devem ser feitas nos TFields do TSQLQuery/TSQLDataSet e você nem precisa de TFields no TClientDataSet para isso (logicamente pode precisar para outras coisas).


Se voce perceber nos meus tópicos anteriores , eu bati o tempo todo nesta tecla.. E O Martins bateu em uma outra direção
Gentilmente , ele me enviou um email com o seu projeto e pude perceber uma diferença entre o que fiz e o que ele fez
No seu exemplo o Martins usou Um [b:945a9e01fb]SqlDataSet[/b:945a9e01fb] em contraMão do [b:945a9e01fb]SqlQuery[/b:945a9e01fb] que estou usando...
De posse disso , para verificar a [b:945a9e01fb]otimização da Instrução Sql[/b:945a9e01fb]([color=darkred:945a9e01fb]Atraves dos ProvinderFlgas)[/color:945a9e01fb] , coloquei Um [color=darkblue:945a9e01fb]SqlMonitor [/color:945a9e01fb] no Projeto e o configurei para me fornecer um [b:945a9e01fb]Arquivo De Log[/b:945a9e01fb]
[color=darkblue:945a9e01fb][b:945a9e01fb]Nesse artigo de Log pude constatar que o Sql esta mesmo OTIMIZADO , sem que se configurrasse os TFILEDS do TSQLDataSet[/b:945a9e01fb][/color:945a9e01fb]
:!: :!: :!: :!:
De posse desta informação ,[b:945a9e01fb] retirei [/b:945a9e01fb]também os TFileds do ClinteDatset e refis a execução , para que un[b:945a9e01fb] Novo [/b:945a9e01fb]Arquivo de log fosse gerado...
:!: :!: :!:
Para minha surpresa , o Arquivo de log gerado também esta[b:945a9e01fb] OTIMIZADO[/b:945a9e01fb]

:idea: :idea: :idea:
[color=darkblue:945a9e01fb][b:945a9e01fb]ORA , sera uma propriedade do TSQLDataSet , que nos bastidores , ja faz esta otimização .... [/b:945a9e01fb][/color:945a9e01fb] :arrow: Não tenho nenhuma biografia a repeito desse comentário, mas fica registrado que [b:945a9e01fb]foi isso que o Arquivo Log me mostrou[/b:945a9e01fb] , quando usei um SQLDataSet , sem fazer nenhuma configuração dos ProviderFlags dos seus DataSets

Veja parte que interresa do Arquivo gerado , usando SqlDataset sem Configurar o ProviderFlags
update ´SALES´ set ´ORDER_STATUS´ = ? where ´PO_NUMBER´ = ?


O mesmo arquivo Gerado usando SQLQuery , sem confighurar Os ProviderFlags
update ´CUSTOMER´ set ´ORDER_STATUS´ = ? where ´CUST_NO´ = ? and ´CUSTOMER´ = ? and ´CONTACT_FIRST´ = ? and ´CONTACT_LAST´ = ? and ´PHONE_NO´ = ? and ´ADDRESS_LINE1´ = ? and ´ADDRESS_LINE2´ is null and ´CITY´ = ? and ´STATE_PROVINCE´ is null and ´COUNTRY´ = ? and ´POSTAL_CODE´ = ? and ´ON_HOLD´ is null


No mais
Correndo o risco de parecer pretencioso por se tratar de um livro do Guinther, se ele não o orienta desta forma, infelizmente, ele está errado.


O livro esta assim sim,

se eu antes de comprar o livro li os vinte artigos do clube delphi sobre dbexpress/DataSnap to na página 15 com esta dificuldade imagine quando passar para um artigo aonde eu não manjar nada , ai que não vou sair do lugar :cry: :cry: :cry: :cry:


GOSTEI 0
Martins

Martins

17/03/2006

Realmente Marco, descuidadamente eu coloquei um [b:0d287582d1]TSQLDataSet[/b:0d287582d1] no lugar de um [b:0d287582d1]TSQLQuery[/b:0d287582d1], refiz agora pouco o exemplo colocando um SQLQuery e funcionou sem erros, mas não fiz como vc, um log para ver a otmização, gostaria de saber qual foi sua conclusão, de qual forma fica melhor otmizado.

Abraço!


GOSTEI 0
Martins

Martins

17/03/2006

Marco, No modelo proposto, ´quem manda´ nas configurações de TFields é o DataSet (TSQLDataSet/TSQLQuery) e não o TClientDataSet. [b:05cef3732e]As alterações na propriedade ProviderFlags devem ser feitas nos TFields do TSQLQuery/TSQLDataSet[/b:05cef3732e] e você nem precisa de TFields no TClientDataSet para isso (logicamente pode precisar para outras coisas). 1. Adicione todos os TFields no TSQLQuery/TSQLDataSet. 2. Faça as mudanças necessárias ao seu propósito. 3. Então, se quiser, adicione os TFields no TClientDataSet. Você verá que os TFields do TClientDataSet terão herdadas as alterações feitas nos TFields do TSQLDataSet/TSQLQuery. Correndo o risco de parecer pretencioso por se tratar de um livro do Guinther, se ele não o orienta desta forma, infelizmente, ele está errado.


Alterei o ProviderFlags apenas nos TFields do TClientDataSet.
INTERBASE - isc_start_transaction INTERBASE - isc_dsql_allocate_statement update ´CUSTOMER´ set ´CONTACT_FIRST´ = ? where ´CUST_NO´ = ? INTERBASE - isc_dsql_prepare INTERBASE - isc_dsql_sql_info INTERBASE - isc_vax_integer INTERBASE - isc_dsql_describe_bind INTERBASE - SQLDialect = 3 INTERBASE - isc_dsql_execute INTERBASE - isc_dsql_free_statement INTERBASE - isc_dsql_free_statement INTERBASE - isc_commit_transaction


Alterei o ProviderFlags no TFields do TSQLQuery apenas
INTERBASE - isc_start_transaction INTERBASE - isc_dsql_allocate_statement update ´CUSTOMER´ set ´CUSTOMER´ = ? where ´CUST_NO´ = ? INTERBASE - isc_dsql_prepare INTERBASE - isc_dsql_sql_info INTERBASE - isc_vax_integer INTERBASE - isc_dsql_describe_bind INTERBASE - SQLDialect = 3 INTERBASE - isc_dsql_execute INTERBASE - isc_dsql_free_statement INTERBASE - isc_dsql_free_statement INTERBASE - isc_commit_transaction


Utilizando TSQLDataSet sem configurar ProviderFlags.
INTERBASE - isc_start_transaction INTERBASE - isc_dsql_allocate_statement update ´CUSTOMER´ set ´CONTACT_FIRST´ = ? where ´CUST_NO´ = ? INTERBASE - isc_dsql_prepare INTERBASE - isc_dsql_sql_info INTERBASE - isc_vax_integer INTERBASE - isc_dsql_describe_bind INTERBASE - SQLDialect = 3 INTERBASE - isc_dsql_execute INTERBASE - isc_dsql_free_statement INTERBASE - isc_dsql_free_statement INTERBASE - isc_commit_transaction


Usando TSQLQuery sem configurar ProviderFlags
INTERBASE - isc_start_transaction INTERBASE - isc_dsql_allocate_statement update ´CUSTOMER´ set ´CONTACT_FIRST´ = ? where ´CUST_NO´ = ? and ´CUSTOMER´ = ? and ´CONTACT_FIRST´ = ? and ´CONTACT_LAST´ = ? and ´PHONE_NO´ = ? and ´ADDRESS_LINE1´ = ? and


Estou :?

Qual a melhor config. para se obter o melhor desempenho usando DbExpress :?:


GOSTEI 0
Vinicius2k

Vinicius2k

17/03/2006

Colegas,

Não existe um modelo ´correto´ de configuração das ProviderFlags dos TFields e do UpdateMode do TDataSetProvider. Tudo depende do caso. Exemplo:
Em um ambiente multi-usuário o usuário A traz o registro X para alteração. No mesmo momento o usuário B também traz o mesmo registro X para alteração.
- Com UpdateMode em upWhereKeyOnly, quem aplicar os updates depois irá sobrepor as alterações do anterior. Isto porque você, provavelmente, definiu pfInKey para o TField da chave primária e, normalmente, ela não é alterada. Prevalecerá a última alteração.
- Com UpdateMode em upWhereAll, quem aplicar os updates depois não conseguirá sobrepor as alterações feitas pelo primeiro usuário. Isto porque na segunda aplicação dos updates, todos os campos já não mais possuem o mesmo valor anterior.
É você quem deve definir isso pois podem existir casos aonde upWhereAll é necessário.

Com relação a seus testes Martins, não consigo comprová-los. Até aonde sei, quaisquer alterações nas ProviderFlags devem ser feitas no DataSet e mesmo usando o TSQLDataSet sem configurações nas ProviderFlags irá resultar em uma instrução com todos os campos no WHERE, logicamente, dependendo de como está configurado o UpdateMode do TDataSetProvider.


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

- Com UpdateMode em upWhereAll, quem aplicar os updates depois não conseguirá sobrepor as alterações feitas pelo primeiro usuário.


Sinceramente , acho dificil entender esta situação...... Estranho pensar que fazer ou não uma alteração em um determinado registro , esta alteração , sera ou não sera aplicada , dependendo de ser antes ou depois de alguem... No meu conceito e entendimento , qualquer alteração tem que ser validada no momento em que ela é aplicada .. Se outro usuário aplicar outras alteraçoes posteriores que prevaleçam as deles , que são ,mais recentes...

Com relação a seus testes Martins, não consigo comprová-los. Até aonde sei, quaisquer alterações nas ProviderFlags devem ser feitas no DataSet e mesmo usando o TSQLDataSet sem configurações nas ProviderFlags irá resultar em uma instrução com todos os campos no WHERE, [b:b004886f8d]logicamente, dependendo de como está configurado o UpdateMode do TDataSetProvider[/b:b004886f8d].


Quando eu me referi sobre o teste que realizei usando o SqlDataSet sem quiasquer alteraçoes no ProviderFlags dos fileds do SqlDataSet , eu coloquei a propriedade UpdateMode do DataSetProvider configurado para
upWhereAll...

:?: :?: :?: :?: :?:
Mas agora voce me deixou em dúvida [b:b004886f8d]vinicius[/b:b004886f8d] , do jeito que voce falou , seria necessário ou Alterar os Flags [color=darkblue:b004886f8d][b:b004886f8d]Ou[/b:b004886f8d][/color:b004886f8d] Colocar a Propriedade UpdateMode do DataSetProvider configurado para upWhereAll...
[b:b004886f8d]Eu estou ate o momento pensando que é necessário as duas condiçoes e não uma ou outra...[/b:b004886f8d]


GOSTEI 0
Vinicius2k

Vinicius2k

17/03/2006

[quote:827be97105=´Marco Salles´]Sinceramente , acho dificil entender esta situação...... Estranho pensar que fazer ou não uma alteração em um determinado registro , esta alteração , sera ou não sera aplicada , dependendo de ser antes ou depois de alguem... No meu conceito e entendimento , qualquer alteração tem que ser validada no momento em que ela é aplicada .. Se outro usuário aplicar outras alteraçoes posteriores que prevaleçam as deles , que são ,mais recentes...[/quote:827be97105]
Um exemplo com dados fictícios:

[b:827be97105]Situação 1:[/b:827be97105]
[list:827be97105][*:827be97105]ProviderFlags de todos os TFields = [pfInUpdate, [b:827be97105]pfInWhere[/b:827be97105]]. Ou seja: Todos os campos estarão aptos a estar presentes na cláusula WHERE.
[*:827be97105]UpdateMode do TDataSetProvider = [b:827be97105]upWhereAll[/b:827be97105]. Ou seja: Todos os TFields marcados com pfInWhere estarão na cláusula WHERE.
[*:827be97105]Registro:
ID  Nome      Cidade  UF
------------------------
 1  Vinicius  Bicas   MG

[*:827be97105]Usuários [b:827be97105]A[/b:827be97105] e [b:827be97105]B[/b:827be97105] trazem o registro para alteração.
[*:827be97105]Usuário [b:827be97105]A[/b:827be97105] altera o nome para [i:827be97105]Marco[/i:827be97105] e aplica os updates.
[*:827be97105]A Midas resolve para a seguinte instrução:
update <TABELA> set
  NOME = "Marco"
where
  ID = 1 and
  NOME = "Vinicius" and
  CIDADE = "Bicas" and
  UF = "MG"

[*:827be97105]Observe que a instrução resolvida pela Midas levará em conta o valor que o campo NOME possuia no momento que foi trazido para o lado do cliente, no caso, [i:827be97105]Vinicius[/i:827be97105]. Se houver correspondência total do registro na tabela com a cláusula WHERE os updates serão aplicados. No nosso caso, tudo certo. A alteração foi salva.
[*:827be97105]Usuário [b:827be97105]B[/b:827be97105] altera o NOME para [i:827be97105]Martins[/i:827be97105] e aplica os updates.
[*:827be97105]A Midas resolve para a seguinte instrução:
update <TABELA> set
  NOME = "Martins"
where
  ID = 1 and
  NOME = "Vinicius" and
  CIDADE = "Bicas" and
  UF = "MG"

[*:827be97105]Os updates não serão aplicados pois não haverá correspodência total no WHERE. O valor atual presente no campo NOME deste registro é [i:827be97105]Marco[/i:827be97105], fruto da alteração realizada pelo usuário [b:827be97105]A[/b:827be97105], porém no momento que ele foi trazido pelo usuário [b:827be97105]B[/b:827be97105] o valor do campo NOME [b:827be97105]era[/b:827be97105] [i:827be97105]Vinicius[/i:827be97105].
Qualquer alteração no registro pelo usuário [b:827be97105]A[/b:827be97105] iria impedir que o os updates do usuário B fossem aplicados porque todos os valores são considerados no WHERE. Neste a caso a alteração feita pelo usuário [b:827be97105]A[/b:827be97105] prevalece.[/list:u:827be97105]

[b:827be97105]Situação 2:[/b:827be97105]
[list:827be97105][*:827be97105]ProviderFlags do TField [b:827be97105]ID[/b:827be97105] = [pfInWhere, pfInKey]. Ou seja: O campo ID estará apto a estar presente na cláusula WHERE e está marcado como sendo CHAVE.
[*:827be97105]Opcionalmente[color=red:827be97105][b:827be97105]*[/b:827be97105][/color:827be97105], ProviderFlags dos demais TFields = [pfInUpdate]. Ou seja: Nenhum dos campos restantes estarão aptos a estar presentes na cláusula WHERE.
[*:827be97105]UpdateMode do TDataSetProvider = [b:827be97105]upWhereKeyOnly[/b:827be97105]. Ou seja: Apenas os TFields marcados com pfInWhere e pfInKey estarão na cláusula Where. No nosso caso, apenas [b:827be97105]ID[/b:827be97105].
[color=red:827be97105][b:827be97105]*[/b:827be97105][/color:827be97105] Devido ao UpdateMode do TDataSetProvider, não faz diferença os TFields restantes estarem ou não aptos a estar presentes no WHERE, já que upWhereKeyOnly estabeleceu que apenas os marcados com pfInKey estariam presentes no WHERE.
[*:827be97105]Registro:
ID  Nome      Cidade  UF
------------------------
 1  Vinicius  Bicas   MG

[*:827be97105]Usuários [b:827be97105]A[/b:827be97105] e [b:827be97105]B[/b:827be97105] trazem o registro para alteração.
[*:827be97105]Usuário [b:827be97105]A[/b:827be97105] altera o nome para [i:827be97105]Marco[/i:827be97105] e aplica os updates.
[*:827be97105]A Midas resolve para a seguinte instrução:
update <TABELA> set
  NOME = "Marco"
where
  ID = 1

[*:827be97105]Apenas a ID é considerada no WHERE e, no nosso caso, os updates são aplicados com sucesso. A alteração foi salva.
[*:827be97105]Usuário [b:827be97105]B[/b:827be97105] altera o campo NOME para [i:827be97105]Martins[/i:827be97105] e aplica os updates.
[*:827be97105]A Midas resolve para a seguinte instrução:
update <TABELA> set
  NOME = "Martins"
where
  ID = 1

[*:827be97105]Os updates serão aplicados pois, mesmo que o usuário [b:827be97105]A[/b:827be97105] já tenha alterado o campo NOME para [i:827be97105]Marco[/i:827be97105], apenas o valor de [b:827be97105]ID[/b:827be97105] é analisado e este não foi alterado. Neste caso, a alteração do usuário [b:827be97105]B[/b:827be97105], sobrepõe a do usuário [b:827be97105]A[/b:827be97105].[/list:u:827be97105]

[quote:827be97105=´Marco Salles´]...seria necessário ou Alterar os Flags ou colocar a propriedade UpdateMode do DataSetProvider configurado para upWhereAll...
Eu estou ate o momento pensando que é necessário as duas condiçoes e não uma ou outra...[/quote:827be97105]
Seu pensamento está correto. O resultado efetivo é fruto da combinação das opções. Espero que isto tenha ficado claro no exemplo acima.

Porém, na [b:827be97105]Situação 1[/b:827be97105], de que resolveria o UpdateMode = upWhereAll, se apenas o TField [b:827be97105]ID[/b:827be97105] estivesse marcado para pfInWhere?
E na [b:827be97105]Situação 2[/b:827be97105], de que resolveria o UpdateMode = upWhereKeyOnly se nenhum TField estivesse marcado para pfInKey?
Em ambas as situações, de que adiantaria os usuários A e B alterarem o valor do TField NOME se ele não estivesse marcado para pfInUpdate?

Você poderá observar que ´n´ resultados podem ser obtidos com combinações diferentes destas opções e por este motivo eu sugiro que você as estude com cuidado.

Bem vindo à Midas! :)


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

[b:9937e8f939]Parabens Vinicus[/b:9937e8f939] , pela clareza que voce escreveu esse exemplo e muito obrigado por repartir conosco esses conhecimentos...[b:9937e8f939] Parabnes mesmo[/b:9937e8f939] :P :P :P

Mas o que me estranha é o Lado do usuário.... Ele não sabe nada de como está configurado o Banco... O que ele quer é apensa duas coisas
a) Gravar suas alteraçoões
b)Ou Alguma mensagem informando a ele que essas alteraçoes não foram salvas por algum motivo

No Caso que voce especificou na[b:9937e8f939] situação 1 [/b:9937e8f939], O usuário que alterar por ultimo não vai conseguir efetivar esta Alteração ... Fiz uns testes é o método AplyUpdates não gera nenhuma exceção . [b:9937e8f939]Quer Dizer , o cara acha que foi Mas não foi.. ,[/b:9937e8f939]
Mas eu acho que algo deve ser informado para ele...
Pergunto :?: :?: :?: :?:
[b:9937e8f939] É gerada alguma exceção nessa situação onde não há correpondencia total do registro na tabela ?????[/b:9937e8f939]


GOSTEI 0
Vinicius2k

Vinicius2k

17/03/2006

Na [b:4ba8741b3c]situação 1[/b:4ba8741b3c] o usuário [b:4ba8741b3c]B[/b:4ba8741b3c] poderá ter uma mensagem ou mesmo tomar uma providência, mas para isto você precisará utilizar o evento [b:4ba8741b3c]OnReconcileError[/b:4ba8741b3c] do TClientDataSet.

A não aplicação de updates do TClientDataSet nunca gera uma exceção. Quaisquer erros devem ser capturados e tratados no evento OnReconcileError.

O método ApplyUpdates retorna ainda o número de erros ocorridos na operação, então um teste simples pode ajudar na decisão de exibir algo ao usuário, cancelar uma transação, etc:
if ClientDataSet1.ApplyUpdates(0) > 0 then
  ShowMessage(´Houve erro.´);



GOSTEI 0
Adriano Santos

Adriano Santos

17/03/2006

[quote:584a94970d=´Marco Salles´][b:584a94970d]Parabens Vinicus[/b:584a94970d] , pela clareza que voce escreveu esse exemplo e muito obrigado por repartir conosco esses conhecimentos...[b:584a94970d] Parabnes mesmo[/b:584a94970d] :P :P :P

Mas o que me estranha é o Lado do usuário.... Ele não sabe nada de como está configurado o Banco... O que ele quer é apensa duas coisas
a) Gravar suas alteraçoões
b)Ou Alguma mensagem informando a ele que essas alteraçoes não foram salvas por algum motivo

No Caso que voce especificou na[b:584a94970d] situação 1 [/b:584a94970d], O usuário que alterar por ultimo não vai conseguir efetivar esta Alteração ... Fiz uns testes é o método AplyUpdates não gera nenhuma exceção . [b:584a94970d]Quer Dizer , o cara acha que foi Mas não foi.. ,[/b:584a94970d]
Mas eu acho que algo deve ser informado para ele...
Pergunto :?: :?: :?: :?:
[b:584a94970d] É gerada alguma exceção nessa situação onde não há correpondencia total do registro na tabela ?????[/b:584a94970d][/quote:584a94970d]


Galera, tô observamdo este tópico e vi outros do [b:584a94970d]Vinicius2K[/b:584a94970d] sobre a MIDAS e confesso que tô virando fã do Vinicius, rsrs...

Cara tenho conversado com uma galera aqui do trampo e decidimos migrar nosso sistema, por isso estou vendo tudo sobre DBX nos tópicos do fórum e este é bem importante pra mim. Minha dúvida neste ponto é a mesma do Marco, como fazemos? O usuário não quer saber se configuramos o providerflags de um jeito ou de outro, quer é que seja alterado e ponto final. Pensei no seguinte [b:584a94970d]Vinicius[/b:584a94970d], é possível bloquear o registro para alteração, ou deixar read only, mandar mensagem para o usuário ou coisa assim???

Vlw


GOSTEI 0
Martins

Martins

17/03/2006

Na [b:94bb7d66e3]situação 1[/b:94bb7d66e3] o usuário [b:94bb7d66e3]B[/b:94bb7d66e3] poderá ter uma mensagem ou mesmo tomar uma providência, mas para isto você precisará utilizar o evento [b:94bb7d66e3]OnReconcileError[/b:94bb7d66e3] do TClientDataSet. A não aplicação de updates do TClientDataSet nunca gera uma exceção. Quaisquer erros devem ser capturados e tratados no evento OnReconcileError. O método ApplyUpdates retorna ainda o número de erros ocorridos na operação, então um teste simples pode ajudar na decisão de exibir algo ao usuário, cancelar uma transação, etc:
if ClientDataSet1.ApplyUpdates(0) > 0 then
  ShowMessage(´Houve erro.´);


[b:94bb7d66e3]Vinicius[/b:94bb7d66e3] não imagina o quanto a sua participação neste tópico está sendo importante para nós, está nos ajudando a entender melhor a tecnologia [b:94bb7d66e3]MIDAS[/b:94bb7d66e3] e nos levando a questionamentos para encontrarmos uma maneira de desenvolver um sistema consistente.

Pensei no seguinte Vinicius, é possível bloquear o registro para alteração, ou deixar read only, mandar mensagem para o usuário ou coisa assim???


Parecido com o q faziamos com o velho Clipper? Tornar um registro exclusivo para fazer alterações e depois liberá-lo?

[b:94bb7d66e3]Vinicius[/b:94bb7d66e3], seria válido usar [b:94bb7d66e3]Reconcile Error Dialog[/b:94bb7d66e3] :?:

Valew!!


GOSTEI 0
Adriano Santos

Adriano Santos

17/03/2006

[b:73c1250abf]Vinicius[/b:73c1250abf] não imagina o quanto a sua participação neste tópico está sendo importante para nós, está nos ajudando a entender melhor a tecnologia [b:73c1250abf]MIDAS[/b:73c1250abf] e nos levando a questionamentos para encontrarmos uma maneira de desenvolver um sistema consistente.


Pode crer, mas acho que o Viniciu2K sabe sim o quanto nos ajuda...rsrs... :D

Parecido com o q faziamos com o velho Clipper? Tornar um registro exclusivo para fazer alterações e depois liberá-lo?


Bem, não programei em clipper, mas imagino isso mesmo. Algo como deixar o registro somente leitura, pois o usuário pode querer visualizar o registro sem alterá-lo. Ou simplesmente não abrir o registro de jeito nenhum, embora ache esta abordagem errada.

[b:73c1250abf]Vinicius[/b:73c1250abf], seria válido usar [b:73c1250abf]Reconcile Error Dialog[/b:73c1250abf] :?:


[b:73c1250abf]Reconcile Error Dialog[/b:73c1250abf] ??? Nem sabia que tinha isso. Como funciona [b:73c1250abf]Martins[/b:73c1250abf]


GOSTEI 0
Vinicius2k

Vinicius2k

17/03/2006

Colegas,

Sobre bloquear o registro (bloqueio pessimista), sim é possível. Porém, este bloqueio deve ser feito pelo servidor (na instrução SELECT) e cada SGBD tem um forma particular de efetuar este bloqueio. Por exemplo, IB/FB:
select ID, NOME from <TABELA> where ID = 1 with lock

O registro com ID = 1 ficará trancado até que a transação que o trouxe para o lado do cliente seja finalizada.
Nestes casos, você precisa envolver o SELECT e o posterior UPDATE dentro da mesma transação. O que não é bom pois você terá uma transação muito longa (terá de aguardar o usuário terminar as alterações).

No ponto de vista ´ideal´, esquecendo como *eu* faço, o diálogo de reconciliação, normalmente, é a forma mais correta de tratar estes problemas.

Particularmente, não utilizo diálogo de reconciliação. Sempre que ocorre algum problema, volto a transação, exibo a mensagem ao usuário e tomo a ação [b:d0b6b243db]raCancel[/b:d0b6b243db] no OnReconcileError.
Deve-se apenas observar que a mensagem ao usuário deve ser esclarecedora do problema ocorrido. Analisando o texto de [b:d0b6b243db]E.Message[/b:d0b6b243db] você, quase sempre, consegue determinar o que ocorreu, como uma violação de PK ou de integridade referencial, por exemplo.

O ´problema´, neste caso, é comigo: eu não deixo usuário tomar decisão. Se houver algum problema, eu mesmo cuido para que a consistência dos dados seja mantida e exibo uma mensagem esclarecedora.


GOSTEI 0
Martins

Martins

17/03/2006

[quote:76741b86da=´Adriano Santos´]
Pode crer, mas acho que o Viniciu2K sabe sim o quanto nos ajuda...rsrs... :D
[/quote:76741b86da]

:D tb acredito nisso, ele está sendo muito parceiro.

[quote:76741b86da=´Adriano Santos´]
Bem, não programei em clipper, mas imagino isso mesmo. Algo como deixar o registro somente leitura, pois o usuário pode querer visualizar o registro sem alterá-lo. Ou simplesmente não abrir o registro de jeito nenhum, embora ache esta abordagem errada.
[/quote:76741b86da]
No Clipper faziamos isso, usuários [b:76741b86da]A[/b:76741b86da] e [b:76741b86da]B[/b:76741b86da], quando o usuário [b:76741b86da]A[/b:76741b86da] acessava um registro para editá-lo, bloqueavamos o registro (tornando ele exclusivo para aquele usuário) e se o usuário [b:76741b86da]B[/b:76741b86da] tentasse fazer alguma operação no registro, informavamos q aquele registro estava sendo usado por outro usuário, e tão logo o registro fosse liberado o outro usuário poderia utilizá-lo.


[quote:76741b86da=´Adriano Santos´]
[b:76741b86da]Vinicius[/b:76741b86da], seria válido usar [b:76741b86da]Reconcile Error Dialog[/b:76741b86da] :?:
[/quote:76741b86da]

[b:76741b86da]Reconcile Error Dialog[/b:76741b86da] ??? Nem sabia que tinha isso. Como funciona [b:76741b86da]Martins[/b:76741b86da]


É um Dialog, ele é exibido para mostrar as exceptions ao usuário e espera uma ação.
procedure TdmMain.cdsCustomerReconcileError(DataSet: TCustomClientDataSet;
  E: EReconcileError; UpdateKind: TUpdateKind;
  var Action: TReconcileAction);
begin
  Action := HandleReconcileError(DataSet, UpdateKind, E);
end;


Só não sei se é válido para erros como ´Key violation.´

[b:76741b86da]Vinicius[/b:76741b86da] vc poderia dar mais uma colher aqui :?:

valew!!!!


GOSTEI 0
Adriano Santos

Adriano Santos

17/03/2006

Blz [b:9e45963e0a]Martins[/b:9e45963e0a] agora acho que entendi perfeitamente, só acho bem feinha a tela do Reconcile Error Dialog, mas acredito que dê pra personalizar fazendo a minha própria tela.

Eu não estou acompanhando o tópico com um programa de exemplo, apenas lendo, mas vou fazer um exemplo completo pra testar tudo que foi instalado, assim eu participo mais ´ativamente´ do tópico. Legal explanarmos tudo isso.


GOSTEI 0
Adriano Santos

Adriano Santos

17/03/2006

[quote:0e36905fba=´Adriano Santos´]...tudo que foi [color=red:0e36905fba][b:0e36905fba]instalado[/b:0e36905fba][/color:0e36905fba]...[/quote:0e36905fba]

Opa, instalado não [b:0e36905fba]falado[/b:0e36905fba], tô doidão. :D


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

deixa a minha ficha cair... vamos por etapa

sobre o problema que de nenhumam mensangem do delphi aparentemente ter sido enviada , no caso da [b:f98410d0a4]situação 1)[/b:f98410d0a4] Tenho algumas coisas a falar

[color=darkblue:f98410d0a4]Lembramos que estamos supondo um Erro , isto é o cara acha que foi mas não foi[/color:f98410d0a4]

1) Dissem por ai que a instrução ApplyUpdates[b:f98410d0a4][color=darkblue:f98410d0a4](0)[/color:f98410d0a4][/b:f98410d0a4] ao ser colocado o zero , não permite erros ... então se algo saiu errado , um erro deveria ser gerado :?: :?: :?: :?:

2)O Martins sugeriu uma caixa de mensagens

3)O Vinicius acresecentou:

Particularmente, não utilizo diálogo de reconciliação. Sempre que ocorre algum problema, volto a transação, exibo a mensagem ao usuário e tomo a ação raCancel no OnReconcileError.


Beleza... So que ai vem o [b:f98410d0a4][color=darkblue:f98410d0a4]´X´ [/color:f98410d0a4][/b:f98410d0a4]da questão:

[b:f98410d0a4]Deve-se apenas observar que a mensagem ao usuário deve ser esclarecedora do problema ocorrido. [/b:f98410d0a4]

Deixa eu ver se entendi:::

Analisando o texto de [b:f98410d0a4]E.Message [/b:f98410d0a4]você, quase sempre, consegue determinar o que ocorreu, como uma violação de PK ou de integridade referencial, por exemplo.


Para enviar uma mensagem esclaredora voce analisa o contexto a e.message , tipo numa instrução case ou if... E dependendo desse texto , voce exibe a mensagem..[b:f98410d0a4] Nesse caso que estamos tratando seria engraçado , mas a mensagem deve ser assim [/b:f98410d0a4]:

[b:f98410d0a4]Olha cara , voce tem que atualizar e so depois fazer estas alteraçoes , mas tem que ser rápido , porque se alguem da Rede fizer primeiro que voce , voce vai receber esta mensagem denovo.... Xiiiiiiiiiiiiiiiiiiiiiiiii[/b:f98410d0a4]

:?: :?: :?: :?: :?: :?: :?:
É Claro que isto é uma brincadeira , mas a mensagem não deve fugir muito desse contexto...Nesse esquema o cara dependendo dos acessos fica sempre amarrado , numa situação pessimista do caso.. [b:f98410d0a4]Ou eu to errado [/b:f98410d0a4]:?: :?: :?:


[color=darkblue:f98410d0a4]Importantissimo :[/color:f98410d0a4]

[b:f98410d0a4]Outro ponto chave vinicius , para a perfeita compreessão do tópico , suponha que
Usuário A , faça alçteraçoes nun registro X

Usuário B : Faz alteraçoes em Tres Registro : X , Y , Z

o usuário A Efetiva Primeiro , o Usuário B efetiva depois

Com estmos vendo as alteraçoes dos Registros X , do usuário B , não sera efetivada... Mas e as outras alteraçoes(Y , Z) Serão efetivadas independemente , ou serão abortadas por uma instrução de ação raCancel no OnReconcileError. ???????[/b:f98410d0a4]


GOSTEI 0
Adriano Santos

Adriano Santos

17/03/2006

[quote:19957788f1=´Marco Salles´]

1) Dissem por ai que a instrução ApplyUpdates[b:19957788f1][color=darkblue:19957788f1](0)[/color:19957788f1][/b:19957788f1] ao ser colocado o zero , não permite erros ... então se algo saiu errado , um erro deveria ser gerado :?: :?: :?: :?:

[/quote:19957788f1]

Marco, me permita fazer uma correção aqui. Pelo que vi nos tópicos que acompanhei do Vinicius e outros colegas também não é que o ApplyUpdates(0) não permite erros, na verdade o ApplyUpdates de modo geral nunca gera exceção, mas se colocarmos ApplyUpdates(-1) ele nunca retornará o erro ocorrido, diferente de ApplyUpdates(0).


[quote:19957788f1=´Marco Salles´]
[b:19957788f1]Olha cara , voce tem que atualizar e so depois fazer estas alteraçoes , mas tem que ser rápido , porque se alguem da Rede fizer primeiro que voce , voce vai receber esta mensagem denovo.... Xiiiiiiiiiiiiiiiiiiiiiiiii[/b:19957788f1]
[/quote:19957788f1]

Boa resposta ao usuário :D

[quote:19957788f1=´Marco Salles´]
:?: :?: :?: :?: :?: :?: :?:
É Claro que isto é uma brincadeira , mas a mensagem não deve fugir muito desse contexto...Nesse esquema o cara dependendo dos acessos fica sempre amarrado , numa situação pessimista do caso.. [b:19957788f1]Ou eu to errado [/b:19957788f1]:?: :?: :?:
[/quote:19957788f1]

Pode crer, aki o usuário vai ficar ´p´ da vida pq não consegue fazer nada com o registro, estamos certos?

[quote:19957788f1=´Marco Salles´]
[color=darkblue:19957788f1]Importantissimo :[/color:19957788f1]

[b:19957788f1]Outro ponto chave vinicius , para a perfeita compreessão do tópico , suponha que
Usuário A , faça alçteraçoes nun registro X

Usuário B : Faz alteraçoes em Tres Registro : X , Y , Z

o usuário A Efetiva Primeiro , o Usuário B efetiva depois

Com estmos vendo as alteraçoes dos Registros X , do usuário B , não sera efetivada... Mas e as outras alteraçoes(Y , Z) Serão efetivadas independemente , ou serão abortadas por uma instrução de ação raCancel no OnReconcileError. ???????[/b:19957788f1]

[/quote:19957788f1]


Essa também quero saber, mas imagino que o controle é individual, ou seja, os registros Y e Z devem ser confirmados normalmente pela MIDAS.


GOSTEI 0
Vinicius2k

Vinicius2k

17/03/2006

O parametro informado no ApplyUpdates é um indicador de se e quando ele deve parar de aplicar os updates caso existam erros:

[b:77a504b397]ApplyUpdates(0)[/b:77a504b397]: não podem haver erros. Qualquer erro que ocorra, a aplicação das atualizações será paralizada.
[b:77a504b397]ApplyUpdates(´n´)[/b:77a504b397]: podem haver ´n´ erros. Quando o número de erros atingir ´n´, a aplicação das atualizações será paralizada.
[b:77a504b397]ApplyUpdates(-1)[/b:77a504b397]: não importa quantos erros existirem, o que puder ser aplicado será.

Nas duas situações aonde é possível que a atualização seja paralizada, o evento OnReconcileError é disparado e você, ou o usuário, tem opção de tomar alguma providência.

Vocês devem compreender que, apesar da prática mais comum ser aplicar os updates imediatamente após os post do registro local, isto não é obrigatório. Você pode inserir 1000 registros e só então enviá-los para o banco de dados, por exemplo. Isto só é muito arriscado no dia-a-dia pois pode haver perda de trabalho, mas é o mais indicado em casos como importação de arquivos texto, por exemplo.

Sobre a dúvida em relação aos registros X, Y, Z cada registro é tratado de forma independente pela Midas. A ação [b:77a504b397]raCancel[/b:77a504b397] aborta apenas o registro que ocasionou o erro de reconciliação, para abortar todo o update você teria que invocar o método CancelUpdates do TClientDataSet.

Só uma opinião particular: [b:77a504b397]vocês estão maximizando o problema de alteração de um mesmo registro por diversos usuários [/b:77a504b397]. Esta situação não deve ser frequente e se for o erro está partindo do projeto.
Na ´vida real´ se seu projeto exige que diversos usuários fiquem alterando os registros uns dos outros o dia todo, pense bem pois foi você que errou no seu projeto.

*EU* só uso bloqueio pessimista quando estritamente necessário e não permito que o usuário tome nenhuma decisão. Se ocorrer algum erro, desfaço tudo e ele que faça novamente. Juntando todos os meus clientes, devo ter cerca de 1200 usuários e cada cliente com uma média de 25 usuários. *EU* não tenho problemas com isso.


GOSTEI 0
Martins

Martins

17/03/2006

[quote:be671a7309=´Adriano Santos´]

Marco, me permita fazer uma correção aqui. Pelo que vi nos tópicos que acompanhei do Vinicius e outros colegas também não é que o ApplyUpdates(0) não permite erros, na verdade o ApplyUpdates de modo geral nunca gera exceção, mas se colocarmos ApplyUpdates(-1) ele nunca retornará o erro ocorrido, diferente de ApplyUpdates(0).
[/quote:be671a7309]

Isso mesmo

[quote:be671a7309=´Marco Salles´]
[b:be671a7309]Olha cara , voce tem que atualizar e so depois fazer estas alteraçoes , mas tem que ser rápido , porque se alguem da Rede fizer primeiro que voce , voce vai receber esta mensagem denovo.... Xiiiiiiiiiiiiiiiiiiiiiiiii[/b:be671a7309]
[/quote:be671a7309]

Gostei dessa!!! :D

[quote:be671a7309=´Marco Salles´]
[color=darkblue:be671a7309]Importantissimo :[/color:be671a7309]

[b:be671a7309]Outro ponto chave vinicius , para a perfeita compreessão do tópico , suponha que
Usuário A , faça alçteraçoes nun registro X

Usuário B : Faz alteraçoes em Tres Registro : X , Y , Z

o usuário A Efetiva Primeiro , o Usuário B efetiva depois

Com estmos vendo as alteraçoes dos Registros X , do usuário B , não sera efetivada... Mas e as outras alteraçoes(Y , Z) Serão efetivadas independemente , ou serão abortadas por uma instrução de ação raCancel no OnReconcileError. ???????[/b:be671a7309]

[/quote:be671a7309]

Também penso o mesmo q o Adriano, acho q Y eZ seriam confirmadas.


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

Também penso o mesmo q o Adriano, acho q Y eZ seriam confirmadas.


[b:df1754af97]Testes simples que eu fiz isto não se confirmou esta hipotese...[/b:df1754af97]

Fiz o seguinte , rodei o projeto e foi na pasta do aplicativo e rodeu o executável

No Registro x Acessei pelo projeto e pelo executável da pasta acessei o mesmo registro x e mais dois outros registros ...Salvei primeiro pelo projeto e depois pelo executável... Gerou o erro como deveria , porem , fechei o projeto e abri de novo e os dois registros Y E Z permaneceram inalterados , contrariando o que supostamente voce acreditaram...


GOSTEI 0
Martins

Martins

17/03/2006

[quote:0316e6b70f=´Marco Salles´]
Também penso o mesmo q o Adriano, acho q Y eZ seriam confirmadas.


[b:0316e6b70f]Testes simples que eu fiz isto não se confirmou esta hipotese...[/b:0316e6b70f]

Fiz o seguinte , rodei o projeto e foi na pasta do aplicativo e rodeu o executável

No Registro x Acessei pelo projeto e pelo executável da pasta acessei o mesmo registro x e mais dois outros registros ...Salvei primeiro pelo projeto e depois pelo executável... Gerou o erro como deveria , porem , fechei o projeto e abri de novo e os dois registros Y E Z permaneceram inalterados , contrariando o que supostamente voce acreditaram...[/quote:0316e6b70f]

:shock:

Foi mesmo [b:0316e6b70f]Marco[/b:0316e6b70f] :?: Poxa pensei o contrário :cry: , mas tudo bem, pelo menos já temos a informação de como se comprta, excelente.

Bem vamos ver agora o q vem para aprendermos mais e mais.


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

Foi mesmo Marco Poxa pensei o contrário , mas tudo bem, pelo menos já temos a informação de como se comprta, excelente. Bem vamos ver agora o q vem para aprendermos mais e mais.


[b:71d62d8f78]Não foi so seu nã .. Foi meu tambémm[/b:71d62d8f78]

Veja o comando de Applypdates que estamos dando

ApplyUpdates(0): não podem haver erros. Qualquer erro que ocorra, a aplicação das atualizações será paralizada.

Pois é ... Sera paralizada.... Para que isto não ocorra , no nosso caso do X,Y,Z temos que dar um comando assim

ApplyUpdates(1):.. Com isso mesmo que se tenha um erro , que no caso foi o acesso pelo usuário B ao Regiistro X , os registros Y e Z seráo atualizados...Isto não é fantático.. Pode testar que dá

Eu testei com dois erros , tres erros alterando o ApplyUpdates e foi D+

Agora , acho que isto tb esta definido , volto a um outro assunto

Só uma opinião particular: vocês estão maximizando o problema de alteração de um mesmo registro por diversos usuários . Esta situação não deve ser frequente e se for o erro está partindo do projeto. Na ´vida real´ se seu projeto exige que diversos usuários fiquem alterando os registros uns dos outros o dia todo, pense bem pois foi você que errou no seu projeto.


Mas olha so uma situação , onde estas ediçoes podem ocorrer e não sei a priori como evitar

Suponha que se tenha um empresa onde cada vendedor faça uma venda
Pode existir uma situação onde em uma determinada venda dois ou mais vendedores , farão a venda de um Mesmo produto... O aplicativo deve então , editar a quantidade do Produto numa Tabela de Estoque desse produto... Isto não geraria um ERRO ::::: Ou eu to enganado ????





ApplyUpdates(´n´): podem haver ´n´ erros. Quando o número de erros atingir ´n´, a aplicação das atualizações será paralizada.
ApplyUpdates(-1): não importa quantos erros existirem, o que puder ser aplicado


GOSTEI 0
Adriano Santos

Adriano Santos

17/03/2006

[quote:41b3df11d8=´Marco Salles´]
Suponha que se tenha um empresa onde cada vendedor faça uma venda
Pode existir uma situação onde em uma determinada venda dois ou mais vendedores , farão a venda de um Mesmo produto... O aplicativo deve então , editar a quantidade do Produto numa Tabela de Estoque desse produto... Isto não geraria um ERRO ::::: Ou eu to enganado ????
[/quote:41b3df11d8]

É verdade, um dos vendedores vai vender mas a quantidade deste produto no estoque vai ficar errada. Neste caso não seria interessante fazer o controle de transação no momento da gravação do registro? ´Bloqueando´ o registro por alguns segundos até que o usuário A ´libere´ o registro?


GOSTEI 0
Martins

Martins

17/03/2006

[quote:2c8f5321d0=´Marco Salles´]
Suponha que se tenha um empresa onde cada vendedor faça uma venda
Pode existir uma situação onde em uma determinada venda dois ou mais vendedores , farão a venda de um Mesmo produto... O aplicativo deve então , editar a quantidade do Produto numa Tabela de Estoque desse produto... Isto não geraria um ERRO ::::: Ou eu to enganado ????
[/quote:2c8f5321d0]

Não sei, sinceramente não sei responder essa, mas acho q depende muito de como foram projetadas as regras de negócio, de como são feitas as transações, coisas assim.


GOSTEI 0
Vinicius2k

Vinicius2k

17/03/2006

Colegas,

A situação proposta em relação ao estoque de um produto é o mais clássico erro sobre como manipular os dados em ambiente multi-usuário.

No caso, o que vocês imaginam? Abrir a tabela de estoque do produto, localizar o produto vendido, alterar sua quantidade e aplicar os updates? Se for isso, com certeza vocês terão problemas e como eu disse antes, o erro partiu do projeto. Isto não deve ser feito.

Esta é a forma correta... Sempre referenciar a quantidade atual e aplicar adição ou subtração da quantidade de entrada ou saida, logicamente utilizando uma instrução SQL:
update 
  ESTOQUEPRODUTO 
set 
  QTDE = QTDE - :qtdevenda 
where 
  IDPRODUTO = :idproduto

Executando esta instrução eu garanto que nunca nenhum de vocês terá problemas com quantidade errada em estoque causada por erros da aplicação.
Se você preferir, pode inserir esta instrução em uma Trigger ´After Insert´ da tabela de itens da venda. Mesma segurança, com mais agilidade.

Lembrem-se que as transações entram em ´filas´ para serem executadas no servidor, então, mesmo que 10 usuários salvem sua venda no mesmo milésimo de segundo, a cada execução a quantidade será atualizada baseando-se na quantidade existente, ou seja: 99,99¬ de chances não haver erro (0,01¬ de chances reservado para ´coisas da informática´). Cada SGBD gerencia esta ´fila´ de forma diferente, mas nunca um mesmo registro é editado ao mesmo tempo por mais de uma transação.

E... Transações. Sempre envolvam operações dependentes dentro da mesma transação. Face a qualquer erro volte toda a transação e exiga o re-trabalho do usuário.
Isto garante que o que você fez (o projeto) sempre funcionará e estará íntegro, independente do aqueles ´bichinhos´ chamados usuários tentarem fazer. :)

Sugestão de leitura:
http://www.comunidade-firebird.org/cflp/downloads/CFLP_O034.PDF
http://www.comunidade-firebird.org/cflp/downloads/CFLP_T032.PDF


GOSTEI 0
Martins

Martins

17/03/2006

:D

Valew [b:5c53ec90d1]Vinicius[/b:5c53ec90d1], agora captei vossa mensagem inestimado guru, hehehe :)

Perfeitamente compreendido, e obrigado por ter indicado material sobre o assunto das transações.

valew :wink:


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

A gente tem que ´sugar´ o cara que ele sabe tudo sobre esse asunto.. Aperta daqui que eu aperto de lá...
Valeu vinicius... Acho que agora fui


GOSTEI 0
Adriano Santos

Adriano Santos

17/03/2006

Esta é a forma correta... Sempre referenciar a quantidade atual e aplicar adição ou subtração da quantidade de entrada ou saida, logicamente utilizando uma instrução SQL:
update 
  ESTOQUEPRODUTO 
set 
  QTDE = QTDE - :qtdevenda 
where 
  IDPRODUTO = :idproduto
Executando esta instrução eu garanto que nunca nenhum de vocês terá problemas com quantidade errada em estoque causada por erros da aplicação. Se você preferir, pode inserir esta instrução em uma Trigger ´After Insert´ da tabela de itens da venda. Mesma segurança, com mais agilidade. Lembrem-se que as transações entram em ´filas´ para serem executadas no servidor, então, mesmo que 10 usuários salvem sua venda no mesmo milésimo de segundo, a cada execução a quantidade será atualizada baseando-se na quantidade existente, ou seja: 99,99¬ de chances não haver erro (0,01¬ de chances reservado para ´coisas da informática´). Cada SGBD gerencia esta ´fila´ de forma diferente, mas nunca um mesmo registro é editado ao mesmo tempo por mais de uma transação. E... Transações. Sempre envolvam operações dependentes dentro da mesma transação. Face a qualquer erro volte toda a transação e exiga o re-trabalho do usuário. Isto garante que o que você fez (o projeto) sempre funcionará e estará íntegro, independente do aqueles ´bichinhos´ chamados usuários tentarem fazer. :)


Posso sugerir um exemplo prático e detalhado Vinicius pra ver se entendi totalmente?

Tabelas do sistema:
Itens de Pedido e Estoque

O usuário inicia o processo de venda. Inclui os produtos A, B e C na tabela Itens de Pedido. No momento em que o pedido é confirmado, ou seja, gravado por completo faço um update na tabela (update sugerido acima) usando o código do produto (presente em Itens de Pedido) como ID certo?? Algo como:


procedure TfPrincipal.Button4Click(Sender: TObject);
var
  Transacao : TTransactionDesc;
begin

  try
    Transacao.TransactionID := 1;
    Transacao.IsolationLevel := xilREADCOMMITTED;

    cdsItensPedido.First;
    while not cdsItensPedido.EOF do
    begin
      //update da sqlQueryAtualiza
      //UPDATE ESTOQUE SET QTDE = :QTDE WHERE ID_PRODUTO = :ID_PRODUTO_VENDIDO;
      with sqlQueryAtualiza do
      begin
        ParamByName(´ID_PRODUTO_VENDIDO´).AsFloat := cdsItensPedido.FieldByName(´CODIGO´).AsFloat;
        ParamByName(´QTDE´).AsFloat := cdsItensPedido.FieldByName(´CODIGO´).AsFloat;

        sqlConnection.StartTransaction(Transacao);
        ExecSql;
        sqlConnection.Commit(Transacao);
      end;
      cdsItensPedido.Next;
    end;
  except on E:EDataBaseError do
    begin
      ShowMessage(´Erro na atualização do estoque:´ + #1313 + E.Message);
      sqlConnection.RollBack(Transacao);
    end;
  end;
end;



Aqui estou controlando a transação sozinho, certo????


GOSTEI 0
Vinicius2k

Vinicius2k

17/03/2006

Quase isso... em um modelo sem Nested Datasets (tudo no ´braço´), relação 1:N entre [b:7e08ebba7b]Pedido[/b:7e08ebba7b] e [b:7e08ebba7b]PedidoItem[/b:7e08ebba7b] algo como:

[list:7e08ebba7b][*:7e08ebba7b]Abra e digite o pedido em [b:7e08ebba7b]cdsPedido[/b:7e08ebba7b].
[*:7e08ebba7b]Digite os ítens deste pedido em [b:7e08ebba7b]cdsPedidoItem[/b:7e08ebba7b].
[*:7e08ebba7b]Abra uma transação.
[*:7e08ebba7b]Salve o pedido (AppyUpdates do [b:7e08ebba7b]cdsPedido[/b:7e08ebba7b]) obtendo a ID do pedido.
[*:7e08ebba7b]Utilize a ID obtida e percorra o [b:7e08ebba7b]cdsPedidoItem[/b:7e08ebba7b] de editando os ítens preechendo o campo destinado a receber a ID do Pedido.
[*:7e08ebba7b]Salve os ítens (ApplyUpdates do [b:7e08ebba7b]cdsPedidoItem[/b:7e08ebba7b]).
[*:7e08ebba7b]Crie uma query com a instrução:
update
  PRODUTOESTOQUE
set
  QTDE = QTDE - :qtdevenda
where
  IDPRODUTO = :idproduto

Atenção para a instrução! A que você propõe não leva em conta a quantidade existente...
[*:7e08ebba7b]Percorra o [b:7e08ebba7b]cdsPedidoItem[/b:7e08ebba7b] passando os parametros [b:7e08ebba7b]qtdevenda[/b:7e08ebba7b] e [b:7e08ebba7b]idproduto[/b:7e08ebba7b] para a query, e executando-a a cada passagem de parametros.
[*:7e08ebba7b]Tudo certo? Feche a transação com commit.
[*:7e08ebba7b]Algum erro? Volte toda a transação desde a aplicação dos updates do [b:7e08ebba7b]cdsPedido[/b:7e08ebba7b].[/list:u:7e08ebba7b]

Um exemplo de código, usando dbExpress, seria este:
...
var 
  Transacao : TTransactionDesc;
begin
  Transacao.TransactionID := 1; 
  Transacao.IsolationLevel := xilREADCOMMITTED;
  try
    SeuSQLConnection.StartTransaction(Transacao);
    if cdsPedido.ApplyUpdates(0) > 0 then
    begin
      cdsPedido.Refresh;
      with cdsPedidoItem do
      begin       
        First;
        while not EOF do
        begin
          Edit;
          FieldByName(´IDPedido´).AsInteger := 
            cdsPedido.FieldByName(´IDPedido´).AsInteger;
          Post;
          Next;
        end;
      end;
      if cdsPedidoItem.AppyUpdates(0) > 0 then
      begin
        with cdsPedidoItem do
        begin       
          First;
          try
            while not EOF do
            begin
              SuaQuery.ParamByName(´idproduto´).AsInteger := 
                FieldByName(´IDProduto´).AsInteger;
              SuaQuery.ParamByName(´qtdevenda´).AsInteger := 
                FieldByName(´QtdeVenda´).AsInteger;
              SuaQuery.ExecSQL;
              Next;
            end;
            // Finalmente o Commit se tudo deu certo.
            SeuSQLConnection.Commit(Transacao); 
          except
            // Mensagem ao usuário. Erro atualizando estoque.
            SeuSQLConnection.Rollback(Transacao);
          end;
        end;
      end
      else
      begin
        // Mensagem ao usuário. Erro gravando ítens.
        SeuSQLConnection.Rollback(Transacao);
      end;
    end
    else
    begin
      // Mensagem ao usuário. Erro granvando pedido.
      SeuSQLConnection.Rollback(Transacao);
    end;
  except
    // Mensagem ao usuário. Erro em algum bloco do código desde a 
    // abertura da transação.
    SeuSQLConnection.Rollback(Transacao);
  end;
end;

Como eu disse, este exemplo é todo ´no braço´. Se você usar Nested Datasets e/ou utilizar uma Trigger para atualizar o estoque no ´After Insert´ da tabela PedidoItem, o código ficará bem mais ´enxuto´.

PS: não testei o código, então perdõe-me algum erro de sintaxe.


GOSTEI 0
Adriano Santos

Adriano Santos

17/03/2006

Quase isso...

Ufa, acho que tô aprendendo...:D

..em um modelo sem Nested Datasets...

Acho que sou ignorante, que isso? Nested Datasets ?

Atenção para a instrução! A que você propõe não leva em conta a quantidade existente...

Eu tinha entendido o esquema do QTDE = QTDE - :QTDE, mas na hora de montar o exemplo esqueci, porém ficou claro pra mim.

...utilizar uma Trigger para atualizar o estoque no ´After Insert´...


Com trigger sei fazer, embora eu seja contra o uso de programação no banco de dados quando se desenvolve um projeto pensando em multi-banco, mas esta é uma outra questão que podíamos discutir mais tarde.

vlw


GOSTEI 0
Vinicius2k

Vinicius2k

17/03/2006

[quote:73cc8957da=´Adriano Santos´]Acho que sou ignorante, que isso? Nested Datasets ?[/quote:73cc8957da]
Resumindo: Mestre/Detalhe ao ´jeito Midas de ser´.
Veja, discussão recente:
http://forum.clubedelphi.net/viewtopic.php?t=75051


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

o que eu não to entendendo da ´discusão´ entre o Adriano e o vinicius é exatamente isso

Vamos supor que se insere dados atraves de um dbgridPedidos e também o DbGridItemProdutos

1)Antes de gerar o Pedido Incio a [b]transação[/b]

2)Para cada que se for inserido na tabela Produtos , na hora do post executo 
update 
  ESTOQUEPRODUTO 
set 
  QTDE = QTDE - :qtdevenda 
where 
  IDPRODUTO = :idproduto

3)apos finalizar dou um ApplyUpdates e por fim finalizo a transação



:?: :?: :?: :?: :?:
Bem , eu sei que pode ter algum erro grave nestar estrutura , mas eu não vejo o porque de ficar nessa estrutura de loop ???? eu não estou conseguindo entender , ja que applyupdates grava tanto os dados da tabela mestre quanto os dados da tabela detalhe e a correção na tabela Estoque pode ser feito dando um post na tabela ItemPedidos :?: :?:

Descupe mas não to captando.... :cry: :cry: :cry:


GOSTEI 0
Adriano Santos

Adriano Santos

17/03/2006

[quote:49546d0fef=´Marco Salles´]o que eu não to entendendo da ´discusão´ entre o Adriano e o vinicius é exatamente isso

Vamos supor que se insere dados atraves de um dbgridPedidos e também o DbGridItemProdutos

1)Antes de gerar o Pedido Incio a [b]transação[/b]

2)Para cada que se for inserido na tabela Produtos , na hora do post executo 
update 
  ESTOQUEPRODUTO 
set 
  QTDE = QTDE - :qtdevenda 
where 
  IDPRODUTO = :idproduto

3)apos finalizar dou um ApplyUpdates e por fim finalizo a transação



:?: :?: :?: :?: :?:
Bem , eu sei que pode ter algum erro grave nestar estrutura , mas eu não vejo o porque de ficar nessa estrutura de loop ???? eu não estou conseguindo entender , ja que applyupdates grava tanto os dados da tabela mestre quanto os dados da tabela detalhe e a correção na tabela Estoque pode ser feito dando um post na tabela ItemPedidos :?: :?:

Descupe mas não to captando.... :cry: :cry: :cry:[/quote:49546d0fef]

E ai Marco belê??
Mano imagina a seguinte situação:

[quote:49546d0fef=´Itens de Pedido´]
Codigo| Descrição | Qtde Comprada
001 | Lápis | 10
002 | Caneta | 1
003 | Borracha | 7
[/quote:49546d0fef]


[quote:49546d0fef=´Produtos do Estoque´]
Codigo | Descrição | Qtde Estocada
001 | Lápis | 20
002 | Caneta | 2
003 | Borracha | 10
[/quote:49546d0fef]

A estrutura proposta para fazer a atualização no estoque proposta pelo Vinicius seria:

[quote:49546d0fef=´Estrutura do update´]
UPDATE
ESTOQUE
SET
QTDE = QTDE - :QTDE_VENDIDA
WHERE
CODIGO = :COD_PROD_VENDA
[/quote:49546d0fef]

Se a gente traduzir isso ficaria

[quote:49546d0fef=´Estrutura do update´]
UPDATE
ESTOQUE
SET
QTDE = QTDE - 10
WHERE
CODIGO = 001
[/quote:49546d0fef]

Ou seja, eu teria que fazer um update para cada produto a ser atualizado no estoque, pois na mesma instrução SQL eu não conseguirir atualizar o estoque dos três produtos de uma só vez. Então a gente faz um LOOP na tabela Itens de Pedido e a cada passagem mudamos os parâmetros e executamos a query.

Estava pensando como você, usando POST, mas isso podem ocorrer erros, pois os registros 001, 002 e 003 estão carregados no cliente/estação dependendo de um POST para serem atualizados. Ora, partindo da explicação do Vinicius temos o seguinte caso:

O usuário A faz a venda dos produtos 001, 002 e 003, nas quantidades 5, 3 e 2 respectivamente. Imagina que pouco antes do post outro usuário faz vendas do mesmo produto em quantidades diferentes e efetua a operação, neste ponto o usuário A faz o post sem levar em conta a quantidade atual. Pimba, erro. O produto 001 tinha 20 no estoque -5 que o usuário B vendeu = 15. Quando o usuário A ´postar´ ele ainda terá o valor 20 do estoque na memória, ou seja, 20 - 10 = 10. Diferente da quantidade que realmente existe no estoque, que deveria ser 5 (10 que o usuário A vendeu e 5 que o usuário B vendeu).

Com o update proposto pelo Vinicius a história é diferente, porque o tempo em que o sistema levará para aplicar as modificações é em milésimos de segundo e você ainda leva em consideração a qtde que está realmente no estoque.

Resumindo: O update vai atualizar a quantidade usando QTDE = QTDE - :VALOR_PASSADO, isso o tempo todo. Você pode ter certeza que nunca ocorrerá problemas de estoque incosistente.

Tô certo Vinícius, nosso grande guru...rsrs :D


GOSTEI 0
Martins

Martins

17/03/2006

[b:63db3ea3b9]Adriano[/b:63db3ea3b9], tb interpretei assim. A cada lançamento fosse disparada uma Trigger no After Insert para ir atualizando o estoque constantemente, para que a quantidade fosse sempre a real, evitanto erros, melhor do que deixar para atualizar e fechar a transação somente no final de todos os lançamentos.

Imagine o seguinte, sem o modelo proposto pelo [b:63db3ea3b9]Vinicius[/b:63db3ea3b9].

[quote:63db3ea3b9=´Produtos Estoque´]
Codigo | Descrição | Qtde Estocada
001..... | Lápis....... | 20
002..... | Caneta.... | 2
003..... | Borracha. | 10 [/quote:63db3ea3b9]

[quote:63db3ea3b9=´Itens Pedido - Usuário A´]
Codigo| Descrição | Qtde Comprada
001.... | Lápis....... | 10
002.... | Caneta.... | 1
003.... | Borracha. | 7
[/quote:63db3ea3b9]

[quote:63db3ea3b9=´Itens Pedido - Usuário B´]
Codigo| Descrição | Qtde Comprada
001.... | Lápis....... | 15
002.... | Caneta.... | 2
003.... | Borracha. | 4
[/quote:63db3ea3b9]

Imaginemos como o Adriano falou q os usuário A e B estão fazendo as vendas dos mesmo produtos, ainda mais nesse período de reinicio das aulas, sem atualizar constantemente o Estoque para ter sempre o REAL, deixando para atualizar somente após todos os lançamentos com um POST.

Para ambos a quantidade no estoque vai ser a q estava antes, no momento da abertura, só q quem fechar a venda por último vai receber a mensagem de erro, vai deixar seu estoque negativo. Por isso a necessidade de se projetar de forma correta um sistema multi-usuário, pq se fosse em um sistema mono-usuário, não haveria esse problema.

Vamos esperar pelas colocações do [b:63db3ea3b9]Vincius[/b:63db3ea3b9], nosso mentor.


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

Amigo adriano , quem disse que seria fácil..

Mas me permite tentar entender...

Esta situação de falta de produto é algo questionavel... Tanto é que no artigo sugerido para a leitura , No Estudo Do Caso1 tem um paragrafo interresante:

´[b:d9edd5bf37]Se por exemplo um cliente chega no caixa com um produto na mão , não há o que ser validado em termos de estoque.. A prova da disponibilidade do Produto esta na mãos do Cliente[/b:d9edd5bf37]´... Então esta soma de quantidades não me convence ....

E outra , não adianta nada atualizar , se o usuário não der um refresh , ele não vai ver a quantidade atual... Lembramos que estamos trabalhando em CacheUpdates com o ClinteDataSet

Por outro LADO , li nun tópico do vinicius o seguinte:

http://forum.devmedia.com.br/viewtopic.php?t=49234&highlight=isolationlevel&sid=121432fbde342a60d8095a842ae3f0d4

citação do viniius que me chamou atenção no outro Tópico:
Em resumo, só se pode controlar as transações no dbExpress se a instrução SQL for executada diretamente como no exemplo acima...


Ai o problema esta na estrutura Do Dbexpress e não na lógica do negócio....

ta meio confuso não ???


GOSTEI 0
Adriano Santos

Adriano Santos

17/03/2006

[quote:94d9886720=´Marco Salles´]
Mas me permite tentar entender...
[/quote:94d9886720]
Permito, claro :D.

[quote:94d9886720=´Marco Salles´]
´Se por exemplo um cliente chega no caixa com um produto na mão , não há o que ser validado em termos de estoque.. A prova da disponibilidade do Produto esta na mãos do Cliente...´
[/quote:94d9886720]
Isso se for um PDV, mesmo assim ainda tem uma outra coisa. Imagina que o cliente chegou no caixa com o produto A, mas tem um outro cliente circulando pela loja que pegou o produto A, ou seja, não tem como você saber quanto tem em estoque pois o cara pode ficar horas na loja e com o produto na mão.
Neste caso de PDV, eu concordo contigo, é bem difícil de controlar. Porém se for um sistema onde o cliente pode comprar via telefone ai a coisa muda. Eu trabalhei em um lugar onde funcionava assim o sistema de vendas.

[quote:94d9886720=´Emissão de Pedido de Vendas´]
[list:94d9886720]
[*:94d9886720]Cliente liga para a empresa e diz que quer 3 caixas de parafusos tipo A.
[*:94d9886720]Usuário abre pedido e começa a inserir os itens de pedido.
[*:94d9886720]Usuário finaliza o pedido de vendas, imprime e emite ordem para o estoque.
[*:94d9886720]Estoque recebe ordem e separa o produto para enviar.
[/list:u:94d9886720]
[/quote:94d9886720]

Ou seja, neste caso não há um cliente na boca do caixa com o produto na mão e como disse anteriormente, mesmo que tivesse como vou saber a quatidade em estoque, já que o cliente B pode estar circulando com o produto na mão e de repente não querer mais levá-lo. E pior, deixá-lo em outra parte qualquer do mercado, tipo um carrefour da vida...danou-se tudo. :D

[quote:94d9886720=´Marco Salles´]
Ta meio confuso não ???
[/quote:94d9886720]

Confuso tah, mas acho que estamos perto de chegar a uma conclusão concreta de tudo.


GOSTEI 0
Vinicius2k

Vinicius2k

17/03/2006

Colegas,

Eu já não estou mais entendendo o que vocês estão discutindo... :shock:

Eu não estou garantindo que o seu estoque estará sempre correto devido a uma pequena proteção ter sido feita na aplicação. Estoque depende de ´n´ outras variantes humanas que não são contempladas num simples enlace de updades e controle transacional.

Eu GARANTO com aquilo que nada passará pela aplicação sem que tenha sido dada sua baixa de estoque. O resto, só Deus sabe :)

Marco, sobre o laço que você diz não ter compreendido, a razão:
Voce grava o pedido, grava seus ítens. [b:565e3be7cc]E a baixa do estoque?[/b:565e3be7cc] Você não vai baixar o estoque só por ter gravado os ítens... não no modelo proposto, aonde existe um campo em alguma tabela responsável por armazenar a quantidade atual.
Faz-se necessário um laço nos ítens baixando em uma outra tabela as quantidades do estoque.

Sobre minha citação em outro tópico, ela sozinha fica um tanto fora de contexto... se você observar, eu estava demonstrando como controlar a transação com dbExpress (sem uso da Midas), executando instruções diretamente. Volto a lembrar: dbExpress é uma coisa, Midas é outra.
Sobre o que eu sei em transações no dbExpress (com e sem Midas), aqui no fórum, leiam:
http://forum.devmedia.com.br/viewtopic.php?t=58547


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

Eu GARANTO com aquilo que nada passará pela aplicação sem que tenha sido dada sua baixa de estoque. O resto, só Deus sabe



Mas é isto que estou batendo na tecla....É isso que entendi....

Marco, sobre o laço que você diz não ter compreendido, a razão: Voce grava o pedido, grava seus ítens. E a baixa do estoque? Você não vai baixar o estoque só por ter gravado os ítens...


Sim , mas apenas o Laço do CdDetlahe ja é o suficiente , e alem disso um Applyupdates No CdMestre ja basta

Sobre o que eu sei em transações no dbExpress (com e sem Midas), aqui no fórum, leiam: http://forum.devmedia.com.br/viewtopic.php?t=58547


Vou ler agora...


GOSTEI 0
Adriano Santos

Adriano Santos

17/03/2006

Marco, sobre o laço que você diz não ter compreendido, a razão: Voce grava o pedido, grava seus ítens. [b:39c519b183]E a baixa do estoque?[/b:39c519b183] Você não vai baixar o estoque só por ter gravado os ítens... não no modelo proposto, aonde existe um campo em alguma tabela responsável por armazenar a quantidade atual. Faz-se necessário um laço nos ítens baixando em uma outra tabela as quantidades do estoque.


É exatamente ai que queria chegar Vinicius, era esta a explicação. E a baixa do estoque? Só porque eu inclui um produto da tabela itens de pedido não significa que preciso debitar do estoque, pois o cara pode simplesmente cancelar a operação.

Como você disse, existem ´n´ operações humanas que podem fazer o estoque, assim como outras coisas dentro do sistema, falharem, é absolutamente normal.

Acho que pra mim as dúvidas estão mais que sanadas.

vlw


GOSTEI 0
Marco Salles

Marco Salles

17/03/2006

gente eu não to dizendo que o não deve ter laço.. Eu to dizendo que um laço apenas o Do CdsDetalhe e um ApplyUpdates Do CdsMestre Basta


Fiz um exmplo de Mestre Detalhe e coloquei o código

no SqlQuery esta assim :

Update PRODUTO
set Saldo=Saldo - :quant
Where
Cod_Produto=:Codigo


procedure TForm1.Button1Click(Sender: TObject);
var
 TransDesc:TTransactionDesc;
begin
TransDesc.TransactionID:=1;
TransDesc.IsolationLevel:=xilREADCOMMITTED;
SQLConnection1.StartTransaction(TransDesc);
try
  clientDataSet1.ApplyUpdates(0); //do mestre
    with clientDataSet2 do // do detalhe
       begin
         First;
            while not eof do //atualização do estoque
              begin
               SQLQuery1.Params[0].asinteger:=
                     clientDataSet2.FieldByName(´Quantidade´).asinteger;
               SQLQuery1.Params[1].AsInteger:=
                      clientDataSet2.FieldByName(´Cod_Produto´).asinteger;
               SQLQuery1.ExecSQL;
               Next;
            end;
      end;
  sqlconnection1.Commit(TransDesc);
except
  sqlConnection1.Rollback(TransDesc);
end;
end;


O Exemplo que voces pasaram tinham dois Applyupdates e dos laçoes eu fiquei pensando para que tudo aquilo ????


GOSTEI 0
Vinicius2k

Vinicius2k

17/03/2006

Se você observar, no modelo que exemplifiquei para o Adriano eu fui bem claro em dizer que estava tudo sendo feito ´no braço´ [b:1e973810a2]sem Nested Datasets (mestre/detalhe)[/b:1e973810a2].

E eu também disse que se fosse usando Nested Datasets e/ou trigger o código ficaria enxuto...


GOSTEI 0
Martins

Martins

17/03/2006

Bem eu já assimilei o q deu para ser assimilado, acho q esse assunto do estoque está encerrado, se houver mais alguma situação para ser apreciada, então vamos lá.

Valew


GOSTEI 0
Martins

Martins

17/03/2006

Se você observar, no modelo que exemplifiquei para o Adriano eu fui bem claro em dizer que estava tudo sendo feito ´no braço´ [b:3a3488e3e7]sem Nested Datasets (mestre/detalhe)[/b:3a3488e3e7]. E eu também disse que se fosse usando Nested Datasets e/ou trigger o código ficaria enxuto...


Correto, essa afirmação pode ser vista nos seguintes trechos.
em um modelo sem Nested Datasets (tudo no ´braço´), relação 1:N entre Pedido e PedidoItem


Como eu disse, este exemplo é todo ´no braço´. Se você usar Nested Datasets e/ou utilizar uma Trigger para atualizar o estoque no ´After Insert´ da tabela PedidoItem, o código ficará bem mais ´enxuto´. PS: não testei o código, então perdõe-me algum erro de sintaxe.



GOSTEI 0
Azimute-al

Azimute-al

17/03/2006

É exatamente isso que preciso.
Obrigado Vinicius!


GOSTEI 0
POSTAR