O retorno conta para vários intervalos em uma única instrução SELECT

9

Eu tenho uma tabela de banco de dados do Postgres fooque, entre outras coisas, possui uma coluna scoreque varia de 0 a 10. Quero uma consulta para retornar o número total de pontuações, o número de pontuações entre 0 e 3, o número de pontuações entre 4 e 6, e o número de pontuações entre 7 e 10. Algo como o seguinte:

SELECT
  COUNT(*) as total,
  COUNT(
    SELECT * from foo where score between 0 and 3;
  ) as low,
  COUNT(
    SELECT * from foo where score between 4 and 6;
  ) as mid,
  COUNT(
    SELECT * from foo where score between 7 and 10;
  ) as high
FROM foo;

Eu tentei isso, mas recebi um erro com o SELECTnas COUNTinstruções. Alguma idéia de como posso fazer isso? Tenho certeza de que existe uma maneira super simples no Postgres. Não consigo descobrir os termos corretos para o Google.

Bryan
fonte

Respostas:

7

Basta usar SUM()instruções condicionais por coluna para cada intervalo numérico. O total pode ser somado usando-se SUM(1), supondo que todos os dados da tabela estejam dentro de um dos intervalos - se não, apenas restrinja-o como com os outros.

select sum(case when score between 0 and 3 then 1 else 0 end) as minrange,
       sum(case when score between 4 and 6 then 1 else 0 end) as midrange,
       sum(case when score between 7 and 10 then 1 else 0 end) as maxrange,
       sum(1) as total
from foo;

Link do SQL Fiddle .

Philᵀᴹ
fonte
8

FILTERCláusula agregada no Postgres 9.4+

Desde o Postgres 9.4, existe uma maneira limpa e rápida (padrão SQL):

SELECT count(*) FILTER (WHERE score BETWEEN 0 AND 3)  AS low
     , count(*) FILTER (WHERE score BETWEEN 4 AND 7)  AS mid
     , count(*) FILTER (WHERE score BETWEEN 8 AND 10) AS high
     , count(*)                                       AS total
FROM   foo;

totalacrescenta-se low, mide high, a não ser nulo ou outros valores estão envolvidos.

Ligações:

Leia também abaixo.

Postgres 9.3-

Existem algumas técnicas:

A @Phil forneceu uma CASEdeclaração da maneira padrão (exceto sum(1), que não é a maneira padrão). Eu gosto de usar um formulário mais curto:

SELECT count(score BETWEEN 0 AND 3  OR NULL) AS low
     , count(score BETWEEN 4 AND 6  OR NULL) AS mid
     , count(score BETWEEN 7 AND 10 OR NULL) AS high
     , count(*)                              AS total
FROM   foo;

Se seus valores forem os definidos na sua pergunta (apenas 0- 10possível), simplifique ainda mais:

SELECT count(score < 4 OR NULL)             AS low
     , count(score BETWEEN 4 AND 6 OR NULL) AS mid
     , count(score > 6 OR NULL)             AS high
     , count(*)                             AS total
FROM   foo;

Um pouco mais curto, um pouco mais rápido.

Diferenças sutis

diferenças sutis quando comparado com sum()a resposta de Phil :

  • Mais importante, por documentação :

    Deve-se observar que, exceto por count, essas funções retornam um valor nulo quando nenhuma linha é selecionada. Em particular, sumsem linhas retorna nulo, não zero, como se poderia esperar, ...

  • count(*) é o caminho padrão e um pouco mais rápido que sum(1). Novamente, nulo vs. 0 se aplica.

Qualquer uma dessas consultas (incluindo as de Phil) conta valores nulos para total. Se isso não for desejável, use:

count(score) AS total_not_null

SQL Fiddle na página 9.3.
db <> mexa aqui na página 10.

Erwin Brandstetter
fonte