Nombre de retours pour plusieurs plages dans une seule instruction SELECT

9

J'ai une table de base de données Postgres fooqui, entre autres, a une colonne pour scorecette plage 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 le SELECTdans les COUNTdéclarations. Des idées comment je peux faire ça? Je suis sûr qu'il existe un moyen super simple dans Postgres. Je n'arrive tout simplement pas à trouver les termes corrects pour Google.

Bryan
la source

Réponses:

7

Utilisez simplement des SUM()instructions conditionnelles 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 l'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;

Lien SQL Fiddle .

Philᵀᴹ
la source
8

FILTERClause d' agrégation 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;

totaladditionne low, midet high, à moins que d' autres valeurs NULL ou sont impliquées.

Liens:

Lisez également ci-dessous.

Postgres 9.3-

Il existe quelques techniques:

@Phil a fourni la méthode standard avec une CASEinstruction (à 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 (uniquement 0- 10possible), 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 de subtiles différences par rapport à sum()dans la 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, sumaucune ligne ne renvoie null, pas zéro comme on pourrait s'y attendre, ...

  • count(*) est 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.

Erwin Brandstetter
la source