Somar valor agrupado PostgreSQL

Olá pessoal…
meu problema é simples mas não consegui resolver ainda…

somente preciso somar os valores retornados de uma consulta, só que essa sql possui group by e esses valores devem ser somados separadamente, ou seja, primeiro o agrupamento deve ser feito e depois uma soma simples dos valores retornados…

minha sql é a seguinte:

select m.meta_auditorias from metas_auditorias m join setores s using (idsetores) join auditorias a on a.setores_idsetores = s.idsetores where a.data between m.data_inicio and m.data_fim group by date_part('month', a.data), m.meta_auditorias
essa sql retorna 8 registros:

8 7 7 8 7 10 8 6
preciso somar estes valores…

parece simples (simplesmente dar um sum e está resolvido), mas assim os dados vão ser primeiro somados e depois agrupados… e o que eu preciso é o inverso (como se tivesse um sum fora dessa sql) mas não adianta pois essa consulta retorna mais de 1 valor…

alguém tem uma ideia de como resolver?
valeu

e se vc colocar o sum assim… sum(m.meta_auditorias) não funciona?

não pois aí ele soma as metas das auditorias daqueles meses separadamente (ou seja, desagrupados…)
o que eu preciso é somar isso tudo agrupado…

o problema dessa soma é justamente o agrupamento… eu preciso primeiro agrupar e DEPOIS somar… e não o inverso…

Qual é a diferença entre somar antes ou somar depois?

Não entendi exatamente o que você quer.

Por que um SUM por fora não resolveria?

Se possível, coloque aqui alguns registros de exemplo destas tabela, indicando o resultado esperado.

bom, o que eu preciso é de uma SQL que traga, num período x, a soma de todas as metas de auditorias…
nisso estão envolvidas as 3 tabelas que seguem em anexo…

como mostra o DER o relacionamento é
auditorias Nx1 setores 1xN metas_auditorias

preciso que, para cada mês/ano do período escolhido (digamos que seja de 01/02/2012 até 30/09/2012) ele pegue a meta de auditoria correspondente a esse período e some no final…
então vai ficar assim: como mostram os dados em anexo, tenho 4 metas de auditorias em períodos diferentes, mas preciso que a SQL some as metas para cada mes no período escolhido…
dessa forma, tenho que retornar as metas de fevereiro até setembro de 2011 e somá-las e depois verificar quantas auditorias foram feitas nesse período para ver se as metas foram alcançadas

e pra isso, eu preciso agrupar as metas por mês/ano para que para cada mês retorne a meta correspondente…
conforme os dados em anexo, neste período teria que retornar as seguintes metas
fevereiro = 10, março = 8, abril = 8, maio = 8, junho = 7, julho = 7, agosto = 7, setembro = 6

isso é fácil, só agrupar por mes naquele período… o único detalhe é que preciso que todas essas metas para esses meses venham somadas… só isso
10 + 8 + 8 + 8 + 7 + 7 + 7 + 6

eu já poderia ter resolvido isso percorrendo essa lista via java e aí somando cada um dos valores, mas o ideal seria resolver tudo em SQL mesmo…

não sei se fui claro…
obrigado pela ajuda, desde já



Ola você pode criar uma função no postgres pra receber esses valores e somar pra vc segue um exemplo uso muitas funções no meu sistema para pegar resultados finais!

Espero que te ajude, ai voce pode colocar todas as somas que vc desejar e retorna o valor especifico para sua aplicação.

[code]CREATE OR REPLACE FUNCTION calcula_valor()
RETURNS numeric AS
$BODY$

DECLARE
Nx1_setores numeric(10,2);
1xN metas_auditorias numeric(10,2);

BEGIN

Nx1_setores = (SELECT //Dai aqui voce coloca a sua select 

1xN metas_auditorias = (SELECT  //SUM Dai aqui vc pode tbem adiciona as tabelas somando e 



RETURN 	(Nx1_setores  + 1xN metas_auditorias );

END

$BODY$
LANGUAGE plpgsql VOLATILE[/code]

[quote=uaslei Java]Ola você pode criar uma função no postgres pra receber esses valores e somar pra vc segue um exemplo uso muitas funções no meu sistema para pegar resultados finais!

Espero que te ajude, ai voce pode colocar todas as somas que vc desejar e retorna o valor especifico para sua aplicação.

[code]CREATE OR REPLACE FUNCTION calcula_valor()
RETURNS numeric AS
$BODY$

DECLARE
Nx1_setores numeric(10,2);
1xN metas_auditorias numeric(10,2);

BEGIN

Nx1_setores = (SELECT //Dai aqui voce coloca a sua select 

1xN metas_auditorias = (SELECT  //SUM Dai aqui vc pode tbem adiciona as tabelas somando e 



RETURN 	(Nx1_setores  + 1xN metas_auditorias );

END

$BODY$
LANGUAGE plpgsql VOLATILE[/code]

[/quote]
um tipo numeric recebendo uma lista de valores?
eu até poderia resolver com função, fazendo um looping que percorresse o resultado e somasse cada um dos valores… mas eu não acredito que não tenha uma forma de resolver isso somente com SQL…

é… tentei de várias formas mas em SQL não consegui resolver da forma que eu queria… então fiz um looping percorrendo os valores mesmo…

create or replace function soma_metas_auditorias(d_ini date, d_fim date, cod_empresa bigint) returns double precision as $soma$ declare meta integer; soma integer; begin soma := 0; for meta in (select meta_auditorias from metas_auditorias me join setores se using (idsetores) join auditorias au on se.idsetores = au.setores_idsetores and au.data between me.data_inicio and me.data_fim and au.data between d_ini and d_fim and se.empresas_idempresas = cod_empresa group by date_part('month', au.data), date_part('year', au.data), meta_auditorias) loop soma := soma + meta; end loop; return soma; end $soma$ language plpgsql volatile;
mas valeu a ajuda mesmo assim…

Você chegou a testar o código que passei?
Faz exatamente isso.

Você chegou a testar o código que passei?
Faz exatamente isso.[/quote]
realmente da certo sim (colocando um alias para o from)…
pelo que eu entendi então toda essa sql seria uma subsql da principal, certo?

então escolhendo um período de 01/01/1991 até 12/12/2222 para a empresa 2 ficaria algo como:

select s.idsetores, s.nome, (select sum(meta_auditorias) from (select m.meta_auditorias from metas_auditorias m join setores s using (idsetores) join auditorias a on a.setores_idsetores = s.idsetores where a.data between m.data_inicio and m.data_fim and a.data between '01/01/1991' and '12/12/2222' group by date_part('month', a.data), date_part('year', a.data), m.meta_auditorias) as soma_metas) as soma_metas, count(idauditorias) from setores s join auditorias a on s.idsetores = a.setores_idsetores and a.data between '01/01/1991' and '12/12/2222' and s.empresas_idempresas = 2 group by s.idsetores, s.nome
obrigado pela ajuda, AbelBueno

Olá novamente…
tem mais um detalhe neste problema… eu preciso que as metas dos meses que não possuem auditorias também sejam somadas… pois assim como está, somente quando houver auditorias no mes a meta vai ser contabilizada, e eu precisaria que a meta daquele mes fosse somada mesmo com o total de auditorias zerado…

tem alguma forma de resolver isso só com SQL?

vc quis dizer isso?

nao adianta…
o que tenho que fazer é “gerar” os meses que nao tenham auditorias para pegar a meta mesmo nao tendo auditoria…

exemplo
mês | meta | total_auditorias
|| 02 || 10 || 05
|| 03 || 10 || 11
|| 04 || 06 || 00
|| 05…

Na verdade, a base da solução é o que pmlm postou mesmo.

O problema é o jeito que está montada o resto da sua query.

Quando usa um LEFT JOIN, a tabela da direita pode vir ou não com resultados.
Se não tiver uma linha correspondente, os campos dela serão NULL.

Mas se você coloca campos da tabela da “direita” no seu where, acaba tendo o mesmo efeito prático do INNER JOIN
(Já que os valores NULL sempre retornarão false em qualquer validação).

Na sua query, analisando por cima, tem o seguinte caso:

Isso já mata a utilidade do LEFT JOIN… você poderia mover essa relação para o join:

metas_auditorias m     
join setores s using (idsetores)    
left join auditorias a 
on a.setores_idsetores = s.idsetores
and a.data between m.data_inicio and m.data_fim  

Logo em seguida, no seu where, você tá filtrando pelo período da auditoria:

a.data between '01/01/1991' and '12/12/2222'

Isso novamente elimina a utilidade do seu LEFT JOIN.
Se você precisa levar em consideração um período fixo da auditoria, ela precisará existir.
Esse trecho você pode resolver com algo assim:

  (a.data is null or (a.data between '01/01/1991' and '12/12/2222') )

Mas isso já pode começar a causar problemas de performance, é bom avaliar.

Enfim, revise o uso da tabela de auditoria na sua query, para a lógica com LEFT JOIN funcionar.

primeiro obrigado pela ajuda…

eu entendo que usando o left join eu teria que reavaliar as condições do where, mas o problema não é exatamente esse… o problema é que mesmo eu usando o left join da maneira certa, os dados que eu precise que venham nao ‘existem’… pois eu preciso ter todos os meses de um período X como resultado, mesmo se não existam auditorias (neste caso, o group by vai agrupar os meses, mas não vai ‘criar’ meses onde não tem auditorias)…

pra isso, criei uma tabela dinâmica de meses, que quando uma meta de auditoria é gravada, uma trigger é disparada executando uma função que percorre esse intervalo de valores e acrescenta os meses deste período… e então depois a consulta se baseia nesta tabela (que chamei de competencia)

segue a função:

CREATE OR REPLACE FUNCTION competencias_por_meta() RETURNS trigger AS $BODY$ declare data date; rec record; begin data := new.data_inicio; while (data < new.data_fim) loop select * into rec from competencias where first_day = data; if not found then insert into competencias values (date_part('year', data), date_part('month', data), data, last_day(data)); end if; data := data + interval '1 month'; end loop; return null; end $BODY$ LANGUAGE plpgsql VOLATILE

e então fiz a sql desta forma (ela ficou grande pelo fato de eu ter que fazer os subselects 2 vezes pois o resultado de um participa do cálculo para a porcenagem junto com o resultado do outro, já que não tem como usar um alias em um SELECT):

[code]select s.idsetores, s.nome,
(select count(idauditorias) from competencias cp left join auditorias on
data between cp.first_day and cp.last_day and setores_idsetores = s.idsetores) as total_auditorias,

(select sum (meta_auditorias) from
(select m.meta_auditorias from competencias cp left join metas_auditorias m on
cp.first_day between m.data_inicio and m.data_fim where m.idsetores = s.idsetores and cp.first_day between ? and ?) as soma_metas)
as soma_metas,

((select count(idauditorias) from competencias cp left join auditorias on
data between cp.first_day and cp.last_day and setores_idsetores = s.idsetores)::double precision * 100 /
(select sum (meta_auditorias) from (select m.meta_auditorias from competencias cp left join metas_auditorias m on
cp.first_day between m.data_inicio and m.data_fim where m.idsetores = s.idsetores and cp.first_day between ? and ?)
as soma_metas)::double precision) as percentual_alcancado from

setores s where s.empresas_idempresas = ? group by s.idsetores, s.nome limit nullif(?, 0)[/code]

assim, o ‘núcleo’ da consulta é a tabela de competências, e não mais a de auditorias…