DbExpress X PacketRecords
Estou tentando utilizar dbExpress ao invés do antigo BDE, porém estou tendo dificuldades. O recurso que preciso manter nas aplicações é o incrementalFetching: somente ler os dados do banco de dados quando for necessário. Assim, quando estou utilizando um dbGrid, somente os registros visíveis na grid são carregados do banco de dados, ao invés de toda a tabela. E apenas quando pressiono PageDown, novos registros são carregados.
Ocorre que estou recebendo a mensagem ´Key Violation´ quando executo PageDown numa grid.
Estou utilizando os componetes TSQLDataSet -> TDataSetProvider -> TClientDataSet.
Configurei
- o ClientDataSet com PacketRecords = 1 (para ler somente um registro por vez)
- o DataSetProvider com Opção poRetainServerOrder porque nunca retornava EOF;
O recurso de fetch incremental aparentemente funciona, pois os dados são rapidamente apresentados, porém enquanto vou pressionando seta para baixo, recebo a mensagem de ´Key Violation´.
Consultando a tabela, observei que existem dois registros com o mesmo campo (Nome da Cidade). Acredito que este deva ser o motivo da mensagem de erro, porém não sei como impedir que o programa apresente esta mensagem de erro, afinal, a chave primaria da tabela é outra (nem envolve este campo)...
Consultei o QC, e existe alguma coisa semelhante documentada no registro 4498, mas ainda sem solução. Entretanto eu acredito que alguém já deva ter achado alguma solução para este problema, pois isso torna inviável a substituição do BDE ao DbExpress.
Para concluir... estou usando Delphi 7 com o Update 1 e driver para MSSQLServer.
Qualquer ajuda é bem vinda, obrigado.
Ocorre que estou recebendo a mensagem ´Key Violation´ quando executo PageDown numa grid.
Estou utilizando os componetes TSQLDataSet -> TDataSetProvider -> TClientDataSet.
Configurei
- o ClientDataSet com PacketRecords = 1 (para ler somente um registro por vez)
- o DataSetProvider com Opção poRetainServerOrder porque nunca retornava EOF;
O recurso de fetch incremental aparentemente funciona, pois os dados são rapidamente apresentados, porém enquanto vou pressionando seta para baixo, recebo a mensagem de ´Key Violation´.
Consultando a tabela, observei que existem dois registros com o mesmo campo (Nome da Cidade). Acredito que este deva ser o motivo da mensagem de erro, porém não sei como impedir que o programa apresente esta mensagem de erro, afinal, a chave primaria da tabela é outra (nem envolve este campo)...
Consultei o QC, e existe alguma coisa semelhante documentada no registro 4498, mas ainda sem solução. Entretanto eu acredito que alguém já deva ter achado alguma solução para este problema, pois isso torna inviável a substituição do BDE ao DbExpress.
Para concluir... estou usando Delphi 7 com o Update 1 e driver para MSSQLServer.
Qualquer ajuda é bem vinda, obrigado.
Gilvanj
Curtidas 0
Respostas
Gandalf.nho
24/10/2004
O índice associado ao nome da Cidade é único? Pode ter corrompido algo que permitiu um registro duplicado naquele campo.
GOSTEI 0
Gilvanj
24/10/2004
Olá, gandalf.nho.
Obrigado pelo retorno.
A mensagem de erro não vem do banco de dados, pois acionei o Profiler (programa de monitoramento do SQL Server) e nem ocorre a tentativa de inserção de dados na tabela. Isto é, o comando Insert nem é enviado para o banco de dados.
Para o campo Nome Cidade há um indice sim, porém dado que o problema não é com o banco de dados acredito que isso não deva compromenter. De qq forma removendo o indice e o erro ainda permanece.
Obrigado pelo retorno.
A mensagem de erro não vem do banco de dados, pois acionei o Profiler (programa de monitoramento do SQL Server) e nem ocorre a tentativa de inserção de dados na tabela. Isto é, o comando Insert nem é enviado para o banco de dados.
Para o campo Nome Cidade há um indice sim, porém dado que o problema não é com o banco de dados acredito que isso não deva compromenter. De qq forma removendo o indice e o erro ainda permanece.
GOSTEI 0
Gilvanj
24/10/2004
Aliás, se eu configurar PacketRecords com -1, o erro não acontece. O problema é que desta forma, consome-se muito tempo carregando todos os registros desta tabela.
GOSTEI 0
Afarias
24/10/2004
O único WA postado para essa QC só serve quando os dados são ReadOnly, ou não se utilize as chaves (inKey) para as atualizações.
|O problema é que desta forma, consome-se muito tempo carregando
|todos os registros desta tabela.
Neste caso vc pode definir melhor as consultas (WHERE) para q os resultados sejam sempre pequenos.
T+
|O problema é que desta forma, consome-se muito tempo carregando
|todos os registros desta tabela.
Neste caso vc pode definir melhor as consultas (WHERE) para q os resultados sejam sempre pequenos.
T+
GOSTEI 0
Gilvanj
24/10/2004
afarias, obrigado pela resposta.
> O único WA postado para essa QC só serve quando os dados são
> ReadOnly, ou não se utilize as chaves (inKey) para as atualizações.
O CDS que estou usando pode ser editado e todos os campos estão com a propriedade ProviderFlags sem o inKey.
> Neste caso vc pode definir melhor as consultas (WHERE) para
> q os resultados sejam sempre pequenos.
O problema é que isso vai exigir realizar alterações em diversos programas. A Borland publicou que a conversão de programas escritos com BDE seriam facilmente convertidos para dbExpress. Se for necessário alterar diversos programas, então isso não será verdade (e realmente creio que não deveria ser necessário).
Obrigado,
> O único WA postado para essa QC só serve quando os dados são
> ReadOnly, ou não se utilize as chaves (inKey) para as atualizações.
O CDS que estou usando pode ser editado e todos os campos estão com a propriedade ProviderFlags sem o inKey.
> Neste caso vc pode definir melhor as consultas (WHERE) para
> q os resultados sejam sempre pequenos.
O problema é que isso vai exigir realizar alterações em diversos programas. A Borland publicou que a conversão de programas escritos com BDE seriam facilmente convertidos para dbExpress. Se for necessário alterar diversos programas, então isso não será verdade (e realmente creio que não deveria ser necessário).
Obrigado,
GOSTEI 0
Afarias
24/10/2004
|O CDS que estou usando pode ser editado e todos os campos estão com
|a propriedade ProviderFlags sem o inKey.
Ainda assim vc tem o erro mencionado?? Que estranho... é isso mesmo?
|O problema é que isso vai exigir realizar alterações em diversos
|programas. A Borland publicou que a conversão de programas escritos
|com BDE seriam facilmente convertidos para dbExpress.
A maior parte deles é ...
|Se for necessário alterar diversos programas, então isso não será
|verdade (e realmente creio que não deveria ser necessário).
Bom, definir bem seu SQL para trazer sempre poucos registros é uma boa prática q vc deveria usar de qualquer forma -- quanto a este seu caso específico, note q vc esbarrou em um BUG e não em uma falta de funcionalidade.
Torço q este bug seja solucionado breve e q resolva assim seu problema e de outras pessoas q desejem usar PacketRecords > 0 (felizmente a QC já existe)
No Delphi 5 (o q eu tenho/uso) esse problema não aprece (pelo menos nunca esbarrei nele)
Outra coisa, vc já experimentou sem a opção poRetainServerOrder para ver se dá certo?
T+
|a propriedade ProviderFlags sem o inKey.
Ainda assim vc tem o erro mencionado?? Que estranho... é isso mesmo?
|O problema é que isso vai exigir realizar alterações em diversos
|programas. A Borland publicou que a conversão de programas escritos
|com BDE seriam facilmente convertidos para dbExpress.
A maior parte deles é ...
|Se for necessário alterar diversos programas, então isso não será
|verdade (e realmente creio que não deveria ser necessário).
Bom, definir bem seu SQL para trazer sempre poucos registros é uma boa prática q vc deveria usar de qualquer forma -- quanto a este seu caso específico, note q vc esbarrou em um BUG e não em uma falta de funcionalidade.
Torço q este bug seja solucionado breve e q resolva assim seu problema e de outras pessoas q desejem usar PacketRecords > 0 (felizmente a QC já existe)
No Delphi 5 (o q eu tenho/uso) esse problema não aprece (pelo menos nunca esbarrei nele)
Outra coisa, vc já experimentou sem a opção poRetainServerOrder para ver se dá certo?
T+
GOSTEI 0
Gilvanj
24/10/2004
>> |O CDS que estou usando pode ser editado e todos os campos estão
>> com |a propriedade ProviderFlags sem o inKey.
> Ainda assim vc tem o erro mencionado?? Que estranho... é isso
> mesmo?
É isso mesmo. Inclusive se configurar providerFlags depois de dar o Open ou antes de dar o Open no cds. Realmente muito estranho.
Procurando pela internet, achei este sugestão:
http://groups.google.com.br/groups?hl=pt-BR&lr=&frame=right&th=545455a983417b76&seekm=3d00f25c_1¬40dnews#link3
Então fiz o que foi sugerido: coloquei a propriedade GetMetaData do SQLDataSet para False. A partir deste momento a mensagem KeyViolation desapareceu. Não sei se isso é um bug. Parece que quando utilizo GetMetaData com True, a configuração do ProviderFlags é ignorada.
>> |Se for necessário alterar diversos programas, então isso não será
>> |verdade (e realmente creio que não deveria ser necessário).
> Bom, definir bem seu SQL para trazer sempre poucos registros é uma > boa prática q vc deveria usar de qualquer forma -- quanto a este seu
> caso específico, note q vc esbarrou em um BUG e não em uma falta
> de funcionalidade.
Vc tem razão. O problema é que nos nossos sistemas utilizávamos TTable para as telas de cadastro. Usando a configuração default no dbExpress (isto é, PacketRecords = -1), quando se dá um Open do SQLTable (equivalente ao TTable) todos os registros são lidos do banco de dados. Isso é muito prejudicial para a aplicação, especialmente num ambiente em rede, onde o servidor fica sobrecarregado, trazendo impaciência para os usuários.
> Torço q este bug seja solucionado breve e q resolva assim seu
> problema e de outras pessoas q desejem usar PacketRecords > 0
> (felizmente a QC já existe)
Obrigado!
> Outra coisa, vc já experimentou sem a opção poRetainServerOrder
> para ver se dá certo?
Sim. O problema ainda assim persiste e além disso traz outro inconveniente: como a ordenação utilizada pelo banco é diferente da ordenação do clientdataset (ou pelo menos as regras de ordenação são diferentes), a navegação de registros apresenta comportamento estranho. Por exemplo: quando navegávamos através de grid, alguns registros simplesmente ´surgiam´ de repente na grid, em posição anterior a atual (navegando com seta para baixo, partindo do primeiro registro). Conclusão, precisamos ativar poRetainServerOrder...
A.Faria, agradeço suas sugestões e ajuda. Felizmente esta dificuldade foi resolvida : )
>> com |a propriedade ProviderFlags sem o inKey.
> Ainda assim vc tem o erro mencionado?? Que estranho... é isso
> mesmo?
É isso mesmo. Inclusive se configurar providerFlags depois de dar o Open ou antes de dar o Open no cds. Realmente muito estranho.
Procurando pela internet, achei este sugestão:
http://groups.google.com.br/groups?hl=pt-BR&lr=&frame=right&th=545455a983417b76&seekm=3d00f25c_1¬40dnews#link3
Então fiz o que foi sugerido: coloquei a propriedade GetMetaData do SQLDataSet para False. A partir deste momento a mensagem KeyViolation desapareceu. Não sei se isso é um bug. Parece que quando utilizo GetMetaData com True, a configuração do ProviderFlags é ignorada.
>> |Se for necessário alterar diversos programas, então isso não será
>> |verdade (e realmente creio que não deveria ser necessário).
> Bom, definir bem seu SQL para trazer sempre poucos registros é uma > boa prática q vc deveria usar de qualquer forma -- quanto a este seu
> caso específico, note q vc esbarrou em um BUG e não em uma falta
> de funcionalidade.
Vc tem razão. O problema é que nos nossos sistemas utilizávamos TTable para as telas de cadastro. Usando a configuração default no dbExpress (isto é, PacketRecords = -1), quando se dá um Open do SQLTable (equivalente ao TTable) todos os registros são lidos do banco de dados. Isso é muito prejudicial para a aplicação, especialmente num ambiente em rede, onde o servidor fica sobrecarregado, trazendo impaciência para os usuários.
> Torço q este bug seja solucionado breve e q resolva assim seu
> problema e de outras pessoas q desejem usar PacketRecords > 0
> (felizmente a QC já existe)
Obrigado!
> Outra coisa, vc já experimentou sem a opção poRetainServerOrder
> para ver se dá certo?
Sim. O problema ainda assim persiste e além disso traz outro inconveniente: como a ordenação utilizada pelo banco é diferente da ordenação do clientdataset (ou pelo menos as regras de ordenação são diferentes), a navegação de registros apresenta comportamento estranho. Por exemplo: quando navegávamos através de grid, alguns registros simplesmente ´surgiam´ de repente na grid, em posição anterior a atual (navegando com seta para baixo, partindo do primeiro registro). Conclusão, precisamos ativar poRetainServerOrder...
A.Faria, agradeço suas sugestões e ajuda. Felizmente esta dificuldade foi resolvida : )
GOSTEI 0
Dopi
24/10/2004
Na verdade acho que quase ninguem usa o PacketRecords dessa maneira. Ele é muito usado em aplicações web, para consultas sequenciais... Um bom exemplo é o Yahoo, que quebra os resultados em várias páginas... essa é a real finalidade do PacketRecords...
Mas realmente deve haver um bug... o ClientDataSet mantem um cache com todos os dados recebidos do Provider, acho que o bug está na hora de adcionar mais dados ao cache já existente...
Já experimentou aumentar o PacketRecord para carregar uma pagina do Grid por vez ? Acho que ficaria até mais eficiente que se comunicar com o BD todo hora...
Mesmo assim, algumas rotinas ´comuns´ em aplicações tipo GRID não são viaveis dessa maneira... Exemplo: Para fazer uma busca incremental no banco de dados (locate). O dado que o usuário quer pode nao estar no cache do ClientDataSet. Nesse caso, como a DBX irá se comportar ? Acho que ela não irá tentar buscar o registro no Dataset ligado ao provider...
Mas realmente deve haver um bug... o ClientDataSet mantem um cache com todos os dados recebidos do Provider, acho que o bug está na hora de adcionar mais dados ao cache já existente...
Já experimentou aumentar o PacketRecord para carregar uma pagina do Grid por vez ? Acho que ficaria até mais eficiente que se comunicar com o BD todo hora...
Mesmo assim, algumas rotinas ´comuns´ em aplicações tipo GRID não são viaveis dessa maneira... Exemplo: Para fazer uma busca incremental no banco de dados (locate). O dado que o usuário quer pode nao estar no cache do ClientDataSet. Nesse caso, como a DBX irá se comportar ? Acho que ela não irá tentar buscar o registro no Dataset ligado ao provider...
GOSTEI 0
Gilvanj
24/10/2004
A intenção de usar PacketRecords > 0 é prover pelo menos o mesmo funcionamento já oferecido pela BDE. Quer dizer, desde o Delphi 1 já podíamos contar com este recurso.
Conforme verifiquei, se usar Locate no clientdataset, ele faz o seguinte:
Suponha que numa tabela de clientes ordenada por nome vc tenha dado o Open e recuperado os primeiros 30 clientes (que começam com a letra ´A´). Se for dado um locate com um cliente que começa com a letra ´X´, o clientdataset executa Nexts com o cursor da tabela (isto é, com o SQLDataSet) até localizar o cliente. Quer dizer, se a tabela tem 100 mil registros, vc carregou somente os 30 primeiros, se executar um locate de um registro que está no final da relação, o clientdataset irá buscar praticamente toda a tabela do banco, independente da propriedade PacketRecords. Isto pode ser verificado no fonte dbclient.pas, no método LocateRecord que chama CheckProviderEOF que por sua vez chama o métod FetchMoreData passando sempre o parâmetro True.
Aparentemente, não me parece a implementação ideal. Acho que o cursor do banco tinha que ser fechado e procurado, digo, deveria reabrir um novo cursor para buscar o cliente que começa com a letra X e somente este registro deveria entrar no cache do clientdataset.
Conforme verifiquei, se usar Locate no clientdataset, ele faz o seguinte:
Suponha que numa tabela de clientes ordenada por nome vc tenha dado o Open e recuperado os primeiros 30 clientes (que começam com a letra ´A´). Se for dado um locate com um cliente que começa com a letra ´X´, o clientdataset executa Nexts com o cursor da tabela (isto é, com o SQLDataSet) até localizar o cliente. Quer dizer, se a tabela tem 100 mil registros, vc carregou somente os 30 primeiros, se executar um locate de um registro que está no final da relação, o clientdataset irá buscar praticamente toda a tabela do banco, independente da propriedade PacketRecords. Isto pode ser verificado no fonte dbclient.pas, no método LocateRecord que chama CheckProviderEOF que por sua vez chama o métod FetchMoreData passando sempre o parâmetro True.
Aparentemente, não me parece a implementação ideal. Acho que o cursor do banco tinha que ser fechado e procurado, digo, deveria reabrir um novo cursor para buscar o cliente que começa com a letra X e somente este registro deveria entrar no cache do clientdataset.
GOSTEI 0
Dopi
24/10/2004
Mas somente esse fato, ´Locate´ , já justifica mudar a lógica da aplicação e substituir os SqlTable por comandos SQL parameterizados...
Eu tb adorava os Grids... :-) Até agora estou brigando com a interface do meu programa para deixar de usar os Grids...
Acho que não há escapatória... a não ser remodelar a interface... Eu sei que dá trabalho, mas o resultado final compensa... Vc poderá oferecer a seus clientes uma nova Versao do seu programa: C/S e nao um simples upgrade...
Eu tb adorava os Grids... :-) Até agora estou brigando com a interface do meu programa para deixar de usar os Grids...
Acho que não há escapatória... a não ser remodelar a interface... Eu sei que dá trabalho, mas o resultado final compensa... Vc poderá oferecer a seus clientes uma nova Versao do seu programa: C/S e nao um simples upgrade...
GOSTEI 0
Gilvanj
24/10/2004
> Mas somente esse fato, ´Locate´ , já justifica mudar a lógica da
> aplicação e substituir os SqlTable por comandos SQL
> parameterizados...
E se imaginarmos campos com Lookup, do tipo, pesquisa incremental. Ou seja, conforme o usuário vai digitando os caracteres, a pesquisa é efetuada. Neste caso, a cada tecla pressionada, seria realmente necessário efetuar uma pesquisa no banco de dados, porém somente recuperando os casos que tem as iniciais conforme aquilo que foi digitado.
> Eu tb adorava os Grids... Até agora estou brigando com a interface do
> meu programa para deixar de usar os Grids...
Desculpa, mas neste caso não concordo com vc. Utilizar grids tem vantagens: o usuário pode, de uma vez só, visualizar diversos registros. E se estes registros estão relacionados (como por exemplo, itens de um pedido) é muito mais prático ver o máximo de itens numa única tela, pois não haverá necessidade de ficar navegando na tela. Mesmo que não há condições de exibir todos os registros na tela, ainda assim, os cliques ou pgDown vão ser em número menor do que se o usuário estivesse visualizando apenas um registro por vez.
> Acho que não há escapatória... a não ser remodelar a interface...
> Eu sei que dá trabalho, mas o resultado final compensa... Vc poderá
> oferecer a seus clientes uma nova Versao do seu programa: C/S e nao
> um simples upgrade...
É verdade... Adaptar sistemas para novas versões de tecnologia sempre nos dão oportunidade para remodelar o projeto. Mas neste caso, vou aguardar a consolidação do .Net e a versão do Delphi para este ambiente, antes de propor maiores alterações.
> aplicação e substituir os SqlTable por comandos SQL
> parameterizados...
E se imaginarmos campos com Lookup, do tipo, pesquisa incremental. Ou seja, conforme o usuário vai digitando os caracteres, a pesquisa é efetuada. Neste caso, a cada tecla pressionada, seria realmente necessário efetuar uma pesquisa no banco de dados, porém somente recuperando os casos que tem as iniciais conforme aquilo que foi digitado.
> Eu tb adorava os Grids... Até agora estou brigando com a interface do
> meu programa para deixar de usar os Grids...
Desculpa, mas neste caso não concordo com vc. Utilizar grids tem vantagens: o usuário pode, de uma vez só, visualizar diversos registros. E se estes registros estão relacionados (como por exemplo, itens de um pedido) é muito mais prático ver o máximo de itens numa única tela, pois não haverá necessidade de ficar navegando na tela. Mesmo que não há condições de exibir todos os registros na tela, ainda assim, os cliques ou pgDown vão ser em número menor do que se o usuário estivesse visualizando apenas um registro por vez.
> Acho que não há escapatória... a não ser remodelar a interface...
> Eu sei que dá trabalho, mas o resultado final compensa... Vc poderá
> oferecer a seus clientes uma nova Versao do seu programa: C/S e nao
> um simples upgrade...
É verdade... Adaptar sistemas para novas versões de tecnologia sempre nos dão oportunidade para remodelar o projeto. Mas neste caso, vou aguardar a consolidação do .Net e a versão do Delphi para este ambiente, antes de propor maiores alterações.
GOSTEI 0
Dopi
24/10/2004
E se imaginarmos campos com Lookup, do tipo, pesquisa incremental. Ou seja, conforme o usuário vai digitando os caracteres, a pesquisa é efetuada. Neste caso, a cada tecla pressionada, seria realmente necessário efetuar uma pesquisa no banco de dados, porém somente recuperando os casos que tem as iniciais conforme aquilo que foi digitado
Vi um software que tratava isso de maneira elegante... em um campo parecido com o Lookup o usuário digitava as iniciais do Cliente e pressionava ENTER, se houvesse mais de um registro com as mesmas iniciais ele era exibido no dropdown list do LookUp.
Desculpa, mas neste caso não concordo com vc. Utilizar grids tem vantagens: o usuário pode, de uma vez só, visualizar diversos registros. E se estes registros estão relacionados (como por exemplo, itens de um pedido) é muito mais prático ver o máximo de itens numa única tela, pois não haverá necessidade de ficar navegando na tela. Mesmo que não há condições de exibir todos os registros na tela, ainda assim, os cliques ou pgDown vão ser em número menor do que se o usuário estivesse visualizando apenas um registro por vez.
Claro, acho que me expressei mal. Nao digo para abolir os Grids, mas usa-los de maneira otimizada.... Porque exibir todos os clientes em um grid de busca ? O Cliente pode definir um filtro antes e ai sim vc apresentar o Grid.
Quanto ao .NET: Eu condicionei a mim mesmo a adoção dessa tecnologia somente se o projeto MONO vier a se tornar uma realidade. Faz falta tb uma IDE .NET do Delphi para Linux...
Estou cansado de ´pular´ toda vez que a M$ diz ´pula´ :-)
GOSTEI 0