Página 1 de 1
Comando SUM
Enviado: 31 Mar 2014 15:37
por Pablo César
Em resposta ao colega Carvalho.
Em 31 Mar 2014 14:17, por MP, Carvalho escreveu:ola.
Tenho um arquivo com apenas dois campos numa base de dados que se chama Leite.dbf
os campos sao:
Data
Quant
Refere-se ao controle diario de retirada de leite de uma fazenda. Coloca a data do dia, e a quantidade de leite.
Agora preciso criar uma consulta entre duas datas, para saber a quantidade de leite tirada entre essas determinadas datas. ex: quanto foi o acumulado de leite entre os dias 01/02/2014 e 28/02/2014.
Sei que a funcao SUM faz a soma, mas nao sei como filtrar entre duas datas. poderia me dar uma dica ?
Vejamos primeiramente a sintaxe do comando SUM:
SUM
Soma expressoes numéricas e coloca o valor em variáveis
───────────────────────────────────────────────────────
Sintaxe
SUM <nLista expr> TO <idLista var>
[<abrangência>] [WHILE <lCondiçao>] [FOR <lCondiçao>]
Argumentos
<nLista expr> é a lista de valores numéricos a serem somados para
cada registro processado.
<idLista var> identifica as variáveis receptoras da soma.
Variáveis que nao existam ou nao sejam visíveis sao criadas como
privadas. <idList var> deve conter o mesmo número de elementos de
<nLista expr>.
<abrangência> é a porçao do arquivo de dados a ser somada (SUM). O
assumido é todos (ALL).
WHILE <lCondiçao> especifica o conjunto de registros a partir do
registro corrente até que a condiçao seja falsa.
FOR <lCondiçao> especifica o conjunto condicional de registros a
serem somados dentro da abrangência dada.
Descriçao
SUM soma uma série de expressoes numéricas e armazena o resultado em
variáveis para uma faixa de registros na área corrente.
Note que <nLista expr> é necessária e nao opcional como em outros
dialetos.
Exemplos
O exemplo seguinte ilustra o uso de SUM:
USE Sales NEW
SUM Price * .10, Amount TO nSum1, nSum2
? nSum1 // Resulta: 151515.00
? nSum2 // Resulta: 150675.00
Para pode somar um período de data, no seu exemplo fariamos assim:
Código: Selecionar todos
Set Date to British
Set Century On
dDta1=Ctod("01/02/2014")
dDta2=Ctod("28/02/2014")
SUM ALL Quant TO nTotal FOR Data>=dDta1 .AND. Data<=dDta2
? nTotal
Uma vez que você captou em duas variáveis através de dois GETs e se o campo Data for do tipo DATA, irá funcionar. Mas da próxima vez tente não colocar nome de campo com a nomenclatura "DATA". Seja bem vindo ao fórum e não fique acanhado de fazer as suas perguntas aqui no fórum (em público).

Espero que consiga visualizar a lógica.
Comando SUM
Enviado: 31 Mar 2014 23:39
por fladimir
Pra efeito de conhecimento segue o mesmo resultado porém usando ao invés do comando SUM a função DBEVAL()
Código: Selecionar todos
Set Date to British
Set Century On
dDta1=Ctod("01/02/2014")
dDta2=Ctod("28/02/2014")
/* Abaixo usaremos a função DBEVAL() no lugar do SUM.
A sintaxe é a seguinte:
DbEval( <bBlock> , ;
[<bForCondition>] , ;
[<bWhileCondition>], ;
[<nNextRecords>] , ;
[<nRecord>] , ;
[<lRest>] ) --> NIL
O primeiro bloco de códigos (bBlock) é o q vc quer fazer, no caso somar o Campo Quant e Armazenar na variável nTotal,
o segundo bloco de código (bForCondition) refere-se a condição de filtragem tipo o FOR do SUM.
Veja a linha abaixo representa o q seria feito com SUM mas usando-se o DBEVAL.
*/
DBEVAL( { || nTotal += Quant }, { || Data>=dDta1 .AND. Data<=dDta2 } )
? nTotal
Comando SUM
Enviado: 01 Abr 2014 00:42
por alxsts
Olá!
É sempre bom pensarmos em otimizar nossas aplicações para que tenham a melhor performance possível.
Quando a cláusula ou parâmetro FOR são usados, força-se a leitura da tabela desde o registro onde o ponteiro estiver posicionado até o final da mesma, avaliando a condição para cada registro lido.
Diferentemente disso, quando se usa a cláusula ou parâmetro WHILE, somente os registros, a partir da posição do ponteiro, onde a condição for verdadeira serão avaliados.
Assim, desde que a tabela seja indexada pelo campo critério da seleção, o ideal é posicionar o ponteiro no primeiro registro que atenda o critério e aplicar a condição enquanto ela for verdadeira.
Código: Selecionar todos
Set Date to British
Set Century On
dDta1=Ctod("01/02/2014")
dDta2=Ctod("28/02/2014")
/* Abaixo usaremos a função DBEVAL() no lugar do SUM.
A sintaxe é a seguinte:
DbEval( <bBlock> , ;
[<bForCondition>] , ;
[<bWhileCondition>], ;
[<nNextRecords>] , ;
[<nRecord>] , ;
[<lRest>] ) --> NIL
O primeiro bloco de códigos (bBlock) é o q vc quer fazer, no caso somar o Campo Quant e Armazenar na variável nTotal,
o segundo bloco de código (bForCondition) refere-se a condição de filtragem tipo o FOR do SUM.
Veja a linha abaixo representa o q seria feito com SUM mas usando-se o DBEVAL.
*/
Use Tabela Shared New
Set Index to idxData // indexado por ordem de data inicial ===> Index On DtoS( dataIni ) To idxData
nTotal := 0.00
If DbSeek( DtoS( dDta1 ) )
DBEVAL( { || nTotal += Quant }, NIL, { || Data>=dDta1 .AND. Data<=dDta2 } ) // notar que o segundo parâmetro ([<bForCondition>]) está com NIL
Endif
? nTotal
Comando SUM
Enviado: 01 Abr 2014 09:18
por Pablo César
Eu tinha pensado indicar algo diferente do que o usuário solicitou. Mas como o DBF parece ser pequeno, se considerar que armazena por ano todos os dias do ano, o SUM não iria ficar pesado. Mas se o arquivo for crescer e acumular vários anos e estes registros precisarão ser processados, isto é, com data muito retroativas, então a sugestão mais indicada seria em indexar o campo DATA em ordem crescente, posicionar-se na primeira data que satisfaz a condição e somar o campo QUANT até que não satisfaça tal condição e sair for do laço de repetição (Do While).
Exemplo:
Código: Selecionar todos
Set Date to British
Set Century On
dDta1=Ctod("01/02/2014")
dDta2=Ctod("28/02/2014")
Use Leite Shared New
If .not. File("PorDta.Ntx")
Index on DtoS(DATA) to PorDta
Endif
Set Index to PorDta
nTotal := 0.00
DbSeek( DtoS( dDta1 ), .T. )
Do While Data>=dDta1 .AND. Data<=dDta2
nTotal:=nTotal+QUANT
Skip
Enddo
? nTotal
Neste meu novo exemplo,
não irá percorrer todos os registros como foi pelo comando SUM, apenas processará os registros em questão. Mas atenção o arquivo de índices, precisará estar sempre atualizado.
Fiz da forma menos sintética para que usuários iniciantes, tenham a oportunidade de entender a lógica.
Alexandre, o seu exemplo precisaria considerar que a DATA não sempre poderá ser sequencial. Isto é, se não achar a data seja por feriado ou dia não laborável ou neste caso: dia que não foi extraído o leite ou não registrado. O seu Seek não achará e dará valor zero. Por isso, deveria utilizar o lSoftSeek como verdadeiro (segundo parâmetro do DbSeek). Mas valeu a sua dica de processar apenas os registros em questão e não todos, claro se o banco de dados for grande.
Comando SUM
Enviado: 01 Abr 2014 11:44
por fladimir
Pábulo não entendi o ultimo jeito q vc colocou não seria o mesmo do alexandre?
Comando SUM
Enviado: 01 Abr 2014 11:53
por Pablo César
Oi fladimir. Não é o mesmo, note que na linha 12 estou usando com a opção de SoftSeek e sem a condição de que tenha sido encontrado exatamente na mesma data.
DbSeek( DtoS( dDta1 ), .T. )
Assim irá posicionar-se na primeira data maior que a data inicial em pesquisa.
E ainda fiz de forma mais extensa para ajudar o entendimento de iniciantes. Visto que o colega teve dificuldades em entender o SUM.
Comando SUM
Enviado: 01 Abr 2014 13:53
por alxsts
Olá!
Pablo César escreveu:DbSeek( DtoS( dDta1 ), .T. )
Perfeito, bem lembrado. Acho que assim tudo fica resolvido. Poderia até simplificar
por
Comando SUM
Enviado: 01 Abr 2014 14:07
por Pablo César
Isso ai, Alexandre.
Comando SUM
Enviado: 02 Abr 2014 20:43
por carvalho
Ola amigo,
muito obrigado pelas dicas. Fucionou perfeitamente, so que tive que tirar
os comandos Set Date British e Set Century On.
Porque com eles, o resultado saia sempre zerado, no caso de deixar só o Set Date British, ele retornava
apenas o campo referente a quant da dat1. Mas ta funcionado beleza. Grato pelo Help.
Comando SUM
Enviado: 02 Abr 2014 21:00
por carvalho
Segue o programinha funcionando : sem o Set Date British e Set Century On. Com eles nao retornava os valores.
Clear
use leite
index on data to arqdat
dt1=ctod(space(8))
dt2=ctod(space(8))
do while .t.
@ 10,10 say "Data Inicial..:" get dt1
@ 11,10 say "Data Final...:" get dt2
read
sum all quant to totalquant for data>=dt1 .and. data<=dt2
? "Total no Periodo..:" +str (totalquant)
if lastkey()=27
exit
endif
enddo
Comando SUM
Enviado: 02 Abr 2014 21:43
por Pablo César
Oi Carvalho, fico contente que tenha servido nossas indicações.
carvalho escreveu:so que tive que tirar
os comandos Set Date British e Set Century On.
Porque com eles, o resultado saia sempre zerado
Atenção. Isto deve ser porque não começou a gravar no dbf com os quatro dígitos e ai muitos registros ao invés de gravar 2014 gravou como 1914. Verifique as datas e corrija fisicamente no seu DBF (faça cópia antes). O ideal é trabalhar com SET Century ON.
Outra questão, se você está usando o SUM, não precisa indexar. E acostume-se a indexar somente apenas quando há necessidade de criar o indice, mas se ele está presente em todo o programa, não há necessidade de re-criá-lo e sim mantê-lo atualizado. Sempre aberto em todas as ocasiões que precise escrever no DBF.
Eu colocaria o
if lastkey()=27 linha antes do sum all...
Comando SUM
Enviado: 02 Abr 2014 23:09
por alxsts
Olá!
Só para constar, as observações que fiz acima valem também para o caso do SUM. Fazendo um SEEK antes do SUM e usando uma condição WHILE, processará apenas os registros que atendem a condição. As observações sobre FOR valem também.