Agrupar resultados do Select por faixas e contá-los

Pessoal estou com um probleminha. Tenho uma tabela com dados sobre MW (massa molecular) e PI de umas proteínas, preciso contar quantas proteínas estão em determinados intervalos, como pI entre 3 e 3,99, 4 e 4,99, sucessivamente até 10, e dentro dessa classificação contar os que tem mw menor que 20000, entre 20000 e 40000, e sucessivamente até maior que 80000. O resultado seria uma tabela como mostrada abaixo:

Já encontrei algumas coisas mas não deu certo, queria ajuda de vocês.

Como está a estrutura das suas tabelas?
Sem isso fico difícil ajudar.
O que foi que já encontrou?

Só utilizo uma tabela, esta possui o id da proteína o PI e a MW.
Eu encontrei um código que quase resolve meu problema, mas ainda não deu certo, acabei esquecendo de colocar.

select case when "MW" < 20000 then  
		'1 - menor que 20'  
	when "MW" between 20000 and 39999 then  
		'2 - de 20 a 40'  
	when "MW" between 40000 and 59999 then  
		'3 - de 40 a 60'
	when "MW" between 60000 and 80000 then  
		'4 - de 60 a 80'
	when "MW" > 80000 then  
		'5 - maior que 80'
	end as faixa  
, count(*) as total
from proteinas.dados WHERE "pI" BETWEEN 3 AND 3.999
group by "faixa" order by "faixa";

Código apenas para PI entre 3 e 3,999, para os outros basta apenas mudar o final. Eu estava querendo buscar cada faixa de PI separadamente e ir colocando em uma outra tabela e no final ter a tabela que eu mostrei no começo, mas isso seria só quando eu resolver o problema desse código, pois ele me dá o que quero, só que quando não há proteínas no intervalo, ele não mostra 0, apenas não mostra nada, e eu preciso do 0.

Eis a tabela com os dados (as colunas são spotid, pi, mw)

dados.txt (5.2 KB)

Veja se isso te ajuda

-- entrada (mw_minimo, mw_maximo, mw_faixa, pi_minimo, pi_maximo, pi_faixa)
create or replace function calcula(integer, integer, integer, integer, integer, integer) returns text as $$
    declare
    mw integer;
    pi integer;
    valor integer;
    output TEXT = '';
    BEGIN
    FOR mw IN
        select i
        from generate_series($1, $2, $3) g(i)
    LOOP
        FOR pi IN
            select j
            from generate_series($4, $5, $6) gs(j)
        LOOP            
            select count(a.*) into valor 
            from teste.a a
            where a.mw between mw and (mw + $3) and a.pi between pi and (pi + $6);
            output = output || ' \t ' || valor;
        END LOOP;
        output = output || chr(10) || chr(13);
    END LOOP;
    return output;
    END;
$$ language plpgsql;

select calcula(0, 100000, 20000, 3, 10, 1);

Muito bom seu código! Porém, aqui não reconheceu o ‘\t’ nem o chr( ), mas de antemão já foi de grande ajuda! Vou ver se consigo arrumar essa última parte! Valeu mesmo!

Qual o banco que vc está usando?

Postgre versão 9.5