Página 1 de 2

Como ordeno um campo numerico

Enviado: 15 Fev 2008 12:12
por maccrow
Ola amigos

Tenho um banco de dados que possui varios campos, dentre eles o campo
ESCALA e GRUPO que são do tipo Char e NUMERO que é do tipo Número.

O sistema mostra esse banco conforme a escala a ser exibida que esta no campo ESCALA. A ordem das linhas é dada por NUMERO. No campo grupo, existem 3 tipos de cargos.

Ou seja, na tela é mostrada a escala e muitas vezes NUMERO fica fora de ordem, sendo necessário arruma-lo manualmente após indexar por ESCALA+GRUPO. A indexação tem de ser essa.

Estou criando um programinha em clipper que arrume esses números mas não faço nem idéia de qual comando usar.

Seria SORT ? Não consegui encontrar exemplos que pudesse tentar corrigir isso.

Alguém pode jogar uma luz nisso ?

Obrigado!

Enviado: 15 Fev 2008 12:25
por Pablo César
Bem vindo ao fórum. Pelo visto você é iniciante em programação Clipper. Eu acho que tendo os fontes do programa, seria bom atacar o problema pela raíz e ver o que causaria a quebra de sequência. Se você tem dois campos que tem caracteristicas diferentes (um númerico e ou tipo caracter) e precisam ser indexados: com certeza para indexar precisar converter tudo para STRING (para o tipo caracter), ficando a indexação por exemplo:

Código: Selecionar todos

INDEX ON ESCALA+STR(GRUPO,<tamanho do campo>,<quantidade decimal do campo>) TO ARQ_NTX
ou

Código: Selecionar todos

INDEX ON ESCALA+STRZERO(GRUPO,<tamanho do campo>,<quantidade decimal do campo>) TO ARQ_NTX
Depois para corrigir essa questão de fora de sequência você precisará gravar no seu DBF:

Código: Selecionar todos

INDEX ON... 

SELE GRUPO
GOTO TOP
VN=1
DO WHILE !EOF()
    REPLACE GRUPO WITH STRZERO(VN,<tamanho do campo>,<quantidade decimal do campo>)
    SKIP
ENDDO
Mas antes de fazer alguma coisa, faça uma cópia para outra pasta por se der algo errado.

Enviado: 15 Fev 2008 12:28
por Clipper
Prezado Colega

Uma das opções é fazer assim :

INDEX ON ALLTRIM(ESCALA)+ALLTRIM(GRUPO)+STR(NUMERO,6) TO ARQIND

Você não disse o tamanho do campo NUMERO mas supondo que ele seja 6 a opção é essa acima.

Lembrando que neste caso o SEEK deverá obedecer essa sintaxe.

Até logo.

Marcelo

Enviado: 15 Fev 2008 12:50
por maccrow
Ola Pablo, obrigado pela resposta e as boas-vindas.

Realmente sou re-iniciante (estudei clipper a muito tempo atras) e agora que estou recomeçando, sempre pesquiso aqui no forum minhas dúvidas. Mas essa não teve jeito...

Eu nao tenho acesso ao fonte do programa...por isso estou criando essa pequena aplicação pois várias vezes isso acontece e arrumar na mão as vezes demora um pouco.

O problema deve ser porque o usuário muitas vezes inclui e exclui linha e devido a algum bug no sistema , não é corrigido a ordem dessas linhas.

Os campos ESCALA e GRUPO são Char, por isso a indexação não é o problema. Precisava mesmo é ordenar o campo =)NUMEROS.

Mas pelo exemplo que vc me deu já sei por onde começar !

So vou alterar pois não é GRUPO que quero alterar e sim o campo NUMERO. :))

Valeu pela luz ... vou fazer uns testes aqui.

Muito obrigado!!!

[ ]s

Enviado: 15 Fev 2008 12:53
por maccrow
OI Marcelo,

Valeu pela dica.

O sistema quando é gerada a escala, adiciona os números automaticamente no campo NUMERO. O problema é que o usuario ao alterar essa escala adicionando ou excluindo linha, vai deixando fora de ordem.

Como não tenho acesso aos fontes para corrigir isso, estou criando um programa que faça isso =).

valeu pela ajuda!

[ ]s

Enviado: 15 Fev 2008 13:14
por gvc
maccrow:

Pelo que eu entendi a ordem de exibição dos dados deve ser
Escala + Numero + Grupo. É isso?

Com a função str(<campo>, <tamanho>, <decimais>) ((que já foi apresentado pelo Pablo César ) vc pode usar número transformados em caracteres para indexar o seu arquivo.
Se a ordem é a que coloquei acima, então:
index on escala + str(numero, 6) + grupo to arqntx1

Ele sai por ordem de escala, dentro por ordem de numero e dentro dele, por ordem de grupo.

Lembre-se de montar a chave de pesquisa respeitando o formato do indice.
Só que se o seu programa é externo ao aplicativo dos usuários, quando eles alterarem o arquivo de dados (DBF), o seu indice (gerado pelo seu programa) vai ficar corrompido.

Espero ter ajudado. Boa sorte.

Enviado: 15 Fev 2008 13:17
por rochinha
Amiguinhos

Ja dizia o velho para da programação, ha quem não goste dele, mas muitos dos livros de Clipper ele era o mentor, José Ramalho.

Este mesmo ja dizia:

Usar poucos ou nenhuma função na geração das chaves, inclusive Alltrim() e DtoC().

No caso de campos caracter é boa a idéia de deixá-lo como está e sim homogeneizar outros campos como numéricos, data, etc usando StrZero() e Dtos().

Alltrim(): náo é indicado pois ele elimina a estrutura do indice, tornando susceptivel a quebras, corrupção e registros que simplesmente podem nem entrar no indice. Desculpe-me o amigo Clipper. mas eu não aconselho.

DtoC(): Ao usar este tipo de função deve-se levar em consideração o SET CENTURY ON/OFF(não tenho base pratica) mas o mesmo pode fazer com que registros com datas 1920, 1930 apareçam ordenados juntamente com registros que tenham data 2020 e 2030, pois somente os dois digitos finais entram no indice.

No caso de usar StrZero() voce compatibiliza o tamanho da chave para todos os registros e o uso de DtoS() confere um numero de data verdadeiro dsprezando o estado do SET CENTURY.

Provavelmente isto que postei não te ajude neste problema pois voce não tem os fontes para altera-los em sua estrutura de ordenação, mas que sirva de dica no futuro.

Enviado: 15 Fev 2008 13:41
por Clipper
Bem, eu pelo menos uso ALLTRIM e muitos casos na composicão de indices e nunca tive problemas, o problema é que esqueci de colocar a função PADR() em conjunto no exemplo que postei.

O certo é : INDEX ON PADR(ALLTRIM(ESCALA),6)+PADR(ALLTRIM((GRUPO),6)+STR(NUMERO,6) TO ARQIND

O SET CENTURY ON/OFF não tem nenhuma função na criação de indices, o SET EPOCH TO sim. Não sei se alguém já se deu ao trabalho de olhar um arquivo DBF em hexadecimal, mas quem se der verá que apesar da estrutura mostrar que os campos DATE tem 8 caracteres o Clipper "mente", na verdade ele tem 10, sendo assim independente de gravarmos 15/02/08 o Clipper vai lá e adiciona o 19 ou o 20 na frente do ano dependo do SET EPOCH, é aí que entra o SET EPOCH TO que especifica que as datas deverão ser consideradas a partir de tal ano.

O SET CENTURY deve ser usado para desconsiderar o SET EPOCH, pois neste caso o Clipper considera o que foi digitado, já que terá sido digitado com 4 digitos.

Na criação de indices que exijam a coversão de um campo tipo data deve se usar a função DTOS() que converte para o padrão ANSI ANO/MES/DIA.

Até logo.

Marcelo

Enviado: 15 Fev 2008 15:04
por rochinha
Amiguinho
Clipper escreveu:SET CENTURY deve ser usado para desconsiderar o SET EPOCH
Explique, pois quero entender. Pois acho que perdi tempo e queimei pestanas em 2000 com o bug do milenio.

Em relação ao uso de DtoS() ao inves de DtoC() se, como voce postou, SET CENTURY não tem nenhuma relação na criação de indices..., responda:

Qual o formato da data armazenada em um indice se eu usar DtoC() se o SET CENTURY estive OFF?

Código: Selecionar todos

SET CENTURY OFF
...
INDEX ON DtoC(CampoData) TO ...

Enviado: 15 Fev 2008 15:53
por Clipper
É óbvio que com CENTURY ON ele irá gravar com digitos e com OFF irá gravar com 2 , só que não entendo o motivo do uso de DTOC() pelo menos pra nós brasileiros em indices, o que digo é que se usar o SET EPOCH não fará diferença em relação ao uso de DTOS().

Até logo.

Marcelo

Enviado: 15 Fev 2008 16:51
por rochinha
Amiguinho

Eu não defendo o uso do DtoC() pois o mesmo no caso de indices só traria problemas.

Defendo o fato de se usar DtoS() pois seu usi não tem nada a ver com nação, e sim com um formato internacional(ou não) de numeração e que não muda seja o SET CENTURY ON ou OFF.

Compile o seguinte exemplo e veja como atua o resultado:

Código: Selecionar todos

cData1 = Date()
cData2 = DtoC( Date() )

? cData1, cData2

Set Century On

? cData1, cData2, DtoS( Date() )

Set Century Off

? cData1, cData2, DtoS( Date() )
O que tento passar é somente um alerta e não empurrar minha experiencia goela abaixo. Eu já sofri, tentando encontrar registros em meus filtros sendo que os mesmos existiam fisicamente na tabela, mas a maldita forma de indexar acabava me ferrando.

Para o codigo acima o resultado sera algo como(aqui no meu PC):

Código: Selecionar todos

02/14/08 02/14/08 
02/14/2008 02/14/08 20080214
02/14/08 02/14/08 20080214
Perceba que o SET CENTURY não afetou em nada o resultado obtido por DtoC() e neste caso ele não deve ser usado em comandos INDEX.

No caso do DtoS() veja que ele retornou a data no formato de sempre e a hierarquia cronologia se manteve.

Imagine que voce queira armazenar a data em um campo caracter apos um DtoC() usando SET Date Brit e depois de um tempo voce muda para Set Date French(exemplo) e continua salvando as mesmas variaveis com DtoC() os registros perderão sua cronologia se voce se basear pelo campo caracter que tem esta data. No campo data fica tudo normal.

Supondo que tenhamos na seguinte data: 07/07/1907, usando SET CENTURY OFF e salvemos esta data com DtoC() daqui um ano quando visualizarmos esta variavel poderemos dizer se ela pertence a 1907 ou 2007. Se tiver boa memoria sim.

No caso do DtoS() ao transformar uma data em caracter sera armazenada toda a estrutura e sua hierarquia, ano, mes e dia e cronologiacamente é impossivel obter um erro de Seek() ao pesquisar uma data se usarmos este esquema.

Quanto ao SET EPOCH, que define uma janela de calculos de datas ele era necessário somente a aplicativos feitos com Summer, no Clipper 5.01 em diante eu usei mas nunca verifiquei se precisava, mas me lembro muito bem de obter erros de calculo principalmente em datas de nascimento se não usasse o mesmo.

Usar DtoC() ou DtoS() não significa preferencia e sim segurança de resultados.

Enviado: 15 Fev 2008 17:01
por Clipper
Deixa pra lá, cada um faz da forma que lhe parece melhor...

Até logo.

Marcelo

Enviado: 15 Fev 2008 18:33
por Pablo César
Dear MacCrow,

deixe-me dar mais um palpite que talvez seja a solução DEFINITIVA para seu problema. Essa numeração a que você se refere serve apenas para exibição ?. Se for, então crie um contador toda vez que precisar dele, sem se preocupar com o que está no DBF (não para procura é claro).

Rgds

Enviado: 15 Fev 2008 18:52
por Toledo
Rochinha, faça um teste da seguinte forma:

Código: Selecionar todos

? Date(), DToC(Date())

Set Century On

? Date(), DToC(Date()), DtoS(Date())

Set Century Off

? Date(), DToC(Date()), DtoS(Date())
O Resultado será:

Código: Selecionar todos

02/14/08 02/14/08
02/14/2008 02/14/2008 20080214
02/14/08 02/14/08 20080214
No seu exemplo, você definiu a variável cData2 com DToC() antes dos comandos Set Century On ou Off.

Mas a razão de não se usar DToC() em chaves de índices é que como a função vai transformar a Data em Caracter, assim um registro com data igual a 10/02/2008 virá antes do registro com data igual a 21/01/2008.

Compile o exemplo abaixo:

Código: Selecionar todos

Clear
Set Century On
Set Date Brit
dData1:=CToD("01/01/2008")
dData2:=CToD("21/01/2008")
dData3:=CToD("10/02/2008")
Priv aStru
aStru:={{"Data","D",8,0}}
DbCreate("Teste.dbf",aStru)
Use Teste New
Append Blank
repl Data With dData1
Append Blank
repl Data With dData2
Append Blank
repl Data With dData3
Index On DToC(Data) To Ind1
Index On DToS(Data) To Ind2
Set Index To Ind1, Ind2
Set Order To 1 //Indice com DToC()
Go Top
@ 10,10 Say "Ordem com DToC()    Ordem com DToS()"
vLin:=11
Do While !EOf()
 @ vLin,10 Say Data Pict "@D"
 Skip
 vLin+=1
EndDo

Set Order To 2 //Indice com DToS()
Go Top
vLin:=11
Do While !EOf()
 @ vLin,30 Say Data Pict "@D"
 Skip
 vLin+=1
EndDo
Inkey(0)
Close All
Retu
O Resultado será:

Código: Selecionar todos

Ordem com DToC()
01/01/2008
10/02/2008
21/01/2008

Ordem com DToS()
01/01/2008
21/01/2008
10/02/2008
Abraços,

Enviado: 15 Fev 2008 20:02
por maccrow
Caros,

Obrigado por todas as respostas.

GVC - Obrigado pela dica do Indice. Na verdade, eu corrigindo os DBF (atualmente faço isso manualmente linha a linha) o sistema gera um novo NTX ao acessar mas não necessariamente precisa ser o mesmo que vou gerar.

Rochinha e Clipper - Entendi a parte da indexação, obrigado pelos exemplos.

Pablo - Não posso criar um contador externo, preciso corrigir o banco mesmo. O seu exemplo da correção funcionou com as devidas alterações, porém como tenho 3 ou mais grupos cadastrados no banco, preciso recomeçar uma nova contagem para cada grupo.

Por exemplo, tenho cadastrado os seguintes grupos no banco CADGRUP:

1 - Faxineiros
2 - Seguranças
3 - Balconista
4 - ....
5 - ....

As vezes posso ter mais grupos cadastrados e com números crescentes.

No banco ESCALA tenho a escala desses funcionários. As escalas vão sendo mantidas armazenadas mesmo as mais antigas. Esse banco tem a seguinte estrutura:

Grupo - Nro do grupo que pertence tal cracha
Numero - Nro que ordena a escala e que preciso ordenar
Cracha - Cracha do funcionario
Escsem - Nro da escala

Então, o banco ESCALA esta ordenado por ESCSEM+GRUPO. Agora o que preciso corrigir é o NUMERO pois o sistema deixa ele fora de ordem.

Escrevi o seguinte código. Com a dica do Pablo, funcionou mas ele colocou o numero na sequencia mas só para o primeiro grupo.

Código: Selecionar todos

do While !EOF()
     if ESCALA->ESCSEM=nEscala
		select CADGRUP
		goto top
		seek GRUPO
		if ESCALA->GRUPO>nGrupo
			select ESCALA
			replace NUMERO with nNumesc
			nNumesc++
			skip
		endif
      endif
enddo
Precisava saber como posso indicar quando ele deve recomeçar a contagem num novo grupo. Sei que devo colocar um IF or DO mas não sei exatamente como fazer isso.

Desculpe , acho que fui meio confuso na minha primeira mensagem... espero que esteja um pouco mais claro agora e me desculpem a confusão que foi gerada. Não era a minha intenção.

Será que tem algum tutorial ou tópico que fale bem sobre os bancos de dados e como manipulá-los ? Sei que não deve ser uma coisa difícil, mas estou fazendo isso na raça e ta dificil de sair.


[ ]s