web-dev-qa-db-fra.com

Nombre de retours pour plusieurs plages dans une seule instruction SELECT

J'ai une table de base de données Postgres foo qui, entre autres choses, a une colonne pour score qui va de 0 à 10. Je veux qu'une requête renvoie le nombre total de scores, le nombre de scores entre 0 et 3, le nombre de scores entre 4 et 6 et le nombre de scores entre 7 et 10. Quelque chose comme ceci:

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;

J'ai essayé cela, mais j'ai eu une erreur avec les SELECT dans les instructions COUNT. Des idées comment je peux faire ça? Je suis sûr qu'il y a un moyen super simple dans Postgres. Je n'arrive pas à trouver les termes corrects pour Google.

9
Bryan

Utilisez simplement les instructions conditionnelles SUM() par colonne pour chaque plage de nombres. Le total peut être additionné en utilisant simplement SUM(1), en supposant que toutes les données du tableau se trouvent dans une des plages - sinon, restreignez-la comme avec les autres.

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;

SQL Fiddle link .

7
Philᵀᴹ

Clause d'agrégation FILTER dans Postgres 9.4+

Depuis Postgres 9.4, il existe une méthode propre et rapide (standard 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;

total additionne low, mid et high, sauf si NULL ou d'autres valeurs sont impliquées.

Liens:

Lisez également ci-dessous.

Postgres 9.3-

Il existe quelques techniques:

@ Phil a fourni la méthode standard avec une instruction CASE (à l'exception de sum(1), qui n'est pas la méthode standard). J'aime utiliser un formulaire plus court:

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 vos valeurs sont telles que définies dans votre question (seulement 0 - 10 Possible), simplifiez davantage:

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 peu plus court, à peine plus rapide.

Différences subtiles

Il y a des différences subtiles par rapport à sum() dans Réponse de Phil :

  • Plus important encore, par documentation :

    Il convient de noter qu'à l'exception de count, ces fonctions renvoient une valeur nulle lorsqu'aucune ligne n'est sélectionnée. En particulier, sum sans ligne retourne null, pas zéro comme on pourrait s'y attendre, ...

  • count(*) is la manière standard et un peu plus rapide que sum(1). Encore une fois, null vs 0 s'applique.

L'une ou l'autre de ces requêtes (y compris celle de Phil) compte des valeurs nulles pour total. Si ce n'est pas souhaitable, utilisez plutôt:

count(score) AS total_not_null

SQL Fiddle en pg 9.3.
db <> violon ici en pg 10.

8
Erwin Brandstetter