Retorno cuenta para múltiples rangos en una sola instrucción SELECT

9

Tengo una tabla de base de datos Postgres fooque, entre otras cosas, tiene una columna para scoreese rango de 0 a 10. Quiero que una consulta devuelva el número total de puntajes, el número de puntajes entre 0 y 3, el número de puntajes entre 4 y 6, y el número de puntajes entre 7 y 10. Algo así como lo siguiente:

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;

Intenté esto, pero recibí un error con el SELECTen las COUNTdeclaraciones. ¿Alguna idea de cómo puedo hacer esto? Estoy seguro de que hay una manera súper simple en Postgres. No puedo entender los términos correctos para Google.

Bryan
fuente

Respuestas:

7

Simplemente use SUM()declaraciones condicionales por columna para cada rango de números. El total se puede sumar simplemente usando SUM(1), suponiendo que todos los datos de la tabla se encuentren dentro de uno de los rangos; de lo contrario, simplemente restringirlo como con los demás.

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;

Enlace de violín SQL .

Philᵀᴹ
fuente
8

FILTERCláusula agregada en Postgres 9.4+

Desde Postgres 9.4 hay una manera limpia y rápida (estándar 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;

totalse suma low, midy high, a no ser nulo o demás valores están involucrados.

Enlaces:

Lea también a continuación.

Postgres 9.3-

Hay un par de técnicas:

@Phil proporcionó la forma estándar con una CASEdeclaración (excepto sum(1), que no es la forma estándar). Me gusta usar una forma más corta:

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;

Si sus valores son como se definen en su pregunta (sólo 0- 10es posible), simplificar aún más:

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;

Un poco más corto, apenas más rápido.

Diferencias sutiles

Hay sutiles diferencias cuando se compara con sum()en la respuesta de Phil :

  • Lo más importante, por documentación :

    Cabe señalar que, a excepción de count, estas funciones devuelven un valor nulo cuando no se seleccionan filas. En particular, sumde ninguna fila devuelve nulo, no cero como cabría esperar ...

  • count(*) es la forma estándar y un poco más rápido que sum(1). De nuevo, se aplica nulo vs. 0.

Cualquiera de estas consultas (incluidas las de Phil) cuenta valores nulos para total. Si eso no es deseable, use en su lugar:

count(score) AS total_not_null

SQL Fiddle en la página 9.3.
db <> violín aquí en la página 10.

Erwin Brandstetter
fuente