261 votes

Comment calculer un pourcentage avec une instruction SQL

J'ai une table SQL Server qui contient les utilisateurs et leurs notes. Pour simplifier les choses, disons qu'il y a 2 colonnes - name & grade . Ainsi, une rangée typique serait Nom : "Jean Dupont", Note : "A".

Je cherche une instruction SQL qui trouvera les pourcentages de toutes les réponses possibles. (A, B, C, etc...) De plus, existe-t-il un moyen de faire cela sans définir toutes les réponses possibles (champ de texte ouvert - les utilisateurs pourraient entrer 'pass/fail', 'none', etc...).

Le résultat final que je recherche est A : 5%, B : 15%, C : 40%, etc...

357voto

Alex Aza Points 29204
  1. Le plus efficace (en utilisant over()).

    select Grade, count(*) * 100.0 / sum(count(*)) over()
    from MyTable
    group by Grade
  2. Universel (toute version de SQL).

    select Grade, count(*) * 100.0 / (select count(*) from MyTable)
    from MyTable
    group by Grade;
  3. Avec le CTE, le moins efficace.

    with t(Grade, GradeCount) 
    as 
    ( 
        select Grade, count(*) 
        from MyTable
        group by Grade
    )
    select Grade, GradeCount * 100.0/(select sum(GradeCount) from t)
    from t;

21 votes

Over() a fonctionné parfaitement sur mon SQL Server 2008, j'ai fait les calculs pour confirmer. Afin d'arrondir à 2 décimales, j'ai utilisé CAST(count( ) * 100.0 / sum(count( )) over() AS DECIMAL(18, 2)). Merci pour le post !

4 votes

Au cas où vous déborderiez sur la multiplication de 100 (par ex. Erreur de dépassement arithmétique lors de la conversion d'une expression en type de données int. ), remplacez-la par une division au dénominateur : cast((count(*) / (sum(count(*)) over() / 100)) AS DECIMAL(18, 2)) as Percentage

0 votes

@RJB Pourquoi devez-vous multiplier par 100.0 et pas seulement par 100 lorsque vous convertissez la sortie en décimal ?

278voto

Jason Points 4241

J'ai testé ce qui suit et cela fonctionne. La réponse de gordyii était proche, mais la multiplication de 100 était mal placée et il manquait des parenthèses.

Select Grade, (Count(Grade)* 100 / (Select Count(*) From MyTable)) as Score
From MyTable
Group By Grade

22 votes

Cela donne un résultat en nombres entiers. La somme des résultats n'est pas égale à 100.

10 votes

Ce n'est pas le plus efficace car la table sera scannée deux fois. De plus, la requête ne sera pas aussi simple s'il y a plus d'une table référencée.

14 votes

@Thunder vous pouvez changer 100 en 100.0 pour les valeurs décimales.

50voto

John Gibb Points 4473

Au lieu d'utiliser un CTE distinct pour obtenir le total, vous pouvez utiliser une fonction fenêtre sans la clause "partition by".

Si vous utilisez :

count(*)

pour obtenir le compte d'un groupe, vous pouvez utiliser :

sum(count(*)) over ()

pour obtenir le compte total.

Par exemple :

select Grade, 100. * count(*) / sum(count(*)) over ()
from table
group by Grade;

Il a tendance à être plus rapide dans mon expérience, mais je pense qu'il pourrait utiliser en interne une table temporaire dans certains cas (j'ai vu "Worktable" lors de l'exécution avec "set statistics io on").

EDIT : Je ne sais pas si mon exemple de requête correspond à ce que vous recherchez, je ne faisais qu'illustrer le fonctionnement des fonctions de fenêtrage.

0 votes

+1. C'est génial. Il peut également être utilisé si à la place de "table" il y a une instruction select.

1 votes

Il utilise une bobine en tempdb qui est la table de travail. Les lectures logiques semblent plus élevées mais ils sont comptés différemment de la normale

3 votes

En fait, le COUNT(*) OVER () dans votre requête renverrait un chiffre qui n'a rien à voir (plus précisément, le nombre de rangées de la base de données de l'entreprise). groupé ensemble de résultats). Vous devez utiliser SUM(COUNT(*)) OVER () à la place.

12voto

Vous devez calculer le total des notes Si c'est SQL 2005 vous pouvez utiliser CTE

    WITH Tot(Total) (
    SELECT COUNT(*) FROM table
    )
    SELECT Grade, COUNT(*) / Total * 100
--, CONVERT(VARCHAR, COUNT(*) / Total * 100) + '%'  -- With percentage sign
--, CONVERT(VARCHAR, ROUND(COUNT(*) / Total * 100, -2)) + '%'  -- With Round
    FROM table
    GROUP BY Grade

1 votes

Bien sûr, cela ne donne que les pourcentages pour les codes de grade présents dans le tableau, et non pour ceux qui pourraient être présents et ne le sont pas. Mais sans une liste définitive des codes de grade pertinents (valides), vous ne pouvez pas faire mieux. D'où le +1 de ma part.

1 votes

La perle cachée pour moi était que vous avez commenté CONVERT.

11voto

Jeremy Points 4188

Vous devez grouper sur le champ de la note. Cette requête devrait vous donner ce que vous recherchez dans presque toutes les bases de données.

    Select Grade, CountofGrade / sum(CountofGrade) *100 
    from
    (
    Select Grade, Count(*) as CountofGrade
    From Grades
    Group By Grade) as sub
    Group by Grade

Vous devez préciser le système que vous utilisez.

2 votes

Puisque vous avez un agrégat ('sum(CountofGrade)') dans la sélection externe, n'avez-vous pas besoin d'une clause group by également ? Et en SQL standard, je pense que vous pourriez utiliser '/ (SELECT COUNT(*) FROM Grades)' pour obtenir le total général.

0 votes

IBM Informix Dynamic Server n'aime pas le SUM nu dans la liste de sélection (bien qu'il donne un message un peu moins qu'utile quand il se plaint). Comme indiqué dans ma réponse et dans mon commentaire précédent, l'utilisation d'une expression de sous-sélection complète dans la liste de sélection fonctionne dans IDS.

0 votes

C'est également mieux parce qu'on peut appliquer un where complexe à la requête interne.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X