web-dev-qa-db-fra.com

Dans Redshift/Postgres, comment compter les lignes qui remplissent une condition?

J'essaie d'écrire une requête qui ne compte que les lignes qui remplissent une condition.

Par exemple, dans MySQL, je l’écrirais comme ceci:

SELECT
    COUNT(IF(grade < 70), 1, NULL)
FROM
    grades
ORDER BY
    id DESC;

Cependant, lorsque j'essaie de le faire sur Redshift, l'erreur suivante est renvoyée:

ERREUR: la fonction si (booléen, entier, "inconnu") n'existe pas

Astuce: Aucune fonction ne correspond aux types de nom et d'argument donnés. Vous devrez peut-être ajouter des conversions de type explicites.

J'ai vérifié la documentation pour les instructions conditionnelles, et j'ai trouvé

NULLIF(value1, value2)

mais il ne compare que valeur1 et valeur2 et si ces valeurs sont égales, il renvoie null.

Je ne pouvais pas trouver une simple déclaration SI et, à première vue, je ne trouvais pas le moyen de faire ce que je voulais faire.

J'ai essayé d'utiliser l'expression CASE, mais je n'obtiens pas les résultats souhaités:

SELECT 
    CASE
        WHEN grade < 70 THEN COUNT(rank)
        ELSE COUNT(rank)
    END
FROM
   grades

Voici comment je veux compter les choses:

  • échoué (grade <70)

  • moyenne (70 <= grade <80)

  • bon (80 <= grade <90)

  • excellent (90 <= grade <= 100)

et voici comment je compte voir les résultats:

+========+=========+======+===========+
| failed | average | good | excellent |
+========+=========+======+===========+
|   4    |    2    |  1   |     4     |
+========+=========+======+===========+

mais j'obtiens ceci:

+========+=========+======+===========+
| failed | average | good | excellent |
+========+=========+======+===========+
|  11    |   11    |  11  |    11     |
+========+=========+======+===========+

J'espère que quelqu'un pourra me diriger dans la bonne direction!

Si cela vous aide, voici quelques exemples d'informations

CREATE TABLE grades(
  grade integer DEFAULT 0,
);

INSERT INTO grades(grade) VALUES(69, 50, 55, 60, 75, 70, 87, 100, 100, 98, 94);
39
ILikeTacos

Tout d’abord, le problème que vous avez ici est que vous dites: "Si la note est inférieure à 70, la valeur de cette expression de casse est count (rank). Sinon, la valeur de cette expression est count (rank). . " Donc, dans les deux cas, vous obtenez toujours la même valeur.

SELECT 
    CASE
        WHEN grade < 70 THEN COUNT(rank)
        ELSE COUNT(rank)
    END
FROM
   grades

count () ne compte que les valeurs non NULL, donc le modèle que vous verrez pour accomplir ce que vous essayez est le suivant:

SELECT 
    count(CASE WHEN grade < 70 THEN 1 END) as grade_less_than_70,
    count(CASE WHEN grade >= 70 and grade < 80 THEN 1 END) as grade_between_70_and_80
FROM
   grades

De cette façon, l'expression de cas sera évaluée à 1 uniquement lorsque l'expression de test est vraie et sera sinon nulle. Ensuite, count () ne comptera que les instances non nulles, c’est-à-dire lorsque l’expression de test est vraie, ce qui devrait vous donner ce dont vous avez besoin.

Edit: notez que c’est exactement la même chose que vous l’aviez écrit à l’origine en utilisant count(if(test, true-value, false-value)), seulement réécrit en tant que count(case when test then true-value end) (et null est le support en valeur fausse, puisqu’un else n’a pas été fourni au cas ).

Postgres 9.4 est sorti quelques mois après cet échange original. Cette version a introduit des filtres agrégés, ce qui permet de rendre les scénarios de ce type un peu plus agréables et plus clairs. Cette réponse obtient toujours quelques votes positifs, donc si vous êtes tombé sur ici et utilisez un nouveau postgres (c'est-à-dire 9.4+), vous voudrez peut-être envisager cette version équivalente:

SELECT
    count(*) filter (where grade < 70) as grade_less_than_70,
    count(*) filter (where grade >= 70 and grade < 80) as grade_between_70_and_80
FROM
   grades
111
yieldsfalsehood

Une autre méthode:

SELECT 
    sum(CASE WHEN grade < 70 THEN 1 else 0 END) as grade_less_than_70,
    sum(CASE WHEN grade >= 70 and grade < 80 THEN 1 else 0 END) as grade_between_70_and_80
FROM
   grades

Fonctionne parfaitement si vous souhaitez regrouper les nombres par une colonne catégorique.

11
user1509107