web-dev-qa-db-fra.com

Comment calculer le pourcentage avec une instruction SQL

J'ai une table SQL Server qui contient les utilisateurs et leurs notes. Par souci de simplicité, disons simplement qu'il y a 2 colonnes - name & grade. Ainsi, une ligne typique serait Nom: "John Doe", Grade: "A".

Je cherche une instruction SQL qui trouvera les pourcentages de toutes les réponses possibles. (A, B, C, etc ...) En outre, existe-t-il un moyen de le faire sans définir toutes les réponses possibles (champ de texte ouvert - les utilisateurs peuvent entrer "réussite/échec", "aucune", etc.)

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

152
Alex

J'ai testé les éléments suivants et cela fonctionne. La réponse de gordyii était proche, mais la multiplication de 100 était au mauvais endroit et il manquait une parenthèse.

Select Grade, (Count(Grade)* 100 / (Select Count(*) From MyTable)) as Score
From MyTable
Group By Grade
208
Jason
  1. Le plus efficace (utiliser over ()).

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

    select Grade, count(*) * 100.0 / (select count(*) from MyTable)
    from MyTable
    group by Grade;
    
  3. Avec 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;
    
187
Alex Aza

Au lieu d'utiliser un CTE distinct pour obtenir le total, vous pouvez utiliser une fonction de 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;

Mon expérience a tendance à être plus rapide, mais je pense que dans certains cas, une table temporaire peut être utilisée en interne (j'ai vu "Worktable" lors de l'exécution avec "set statistics io on").

EDIT: Je ne suis pas sûr que mon exemple de requête corresponde à ce que vous recherchez, je viens d’illustrer le fonctionnement des fonctions de fenêtrage.

38
John Gibb

Vous devez calculer le total des notes S'il s'agit de 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

Vous devez grouper sur le champ de qualité. Cette requête devrait vous donner ce que vous cherchez dans à peu près n'importe quelle base 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 spécifier le système que vous utilisez.

9
Jeremy

J'utilise tout simplement cela chaque fois que j'ai besoin de calculer un pourcentage ..

ROUND(CAST((Numerator * 100.0 / Denominator) AS FLOAT), 2) AS Percentage

Notez que 100.0 retourne les décimales, alors que 100 seul arrondira le résultat au nombre entier le plus proche, même avec la fonction ROUND ()!

7
Fandango68

Ce qui suit devrait fonctionner

ID - Key
Grade - A,B,C,D...

EDIT: Déplacement du * 100 et ajout du 1.0 pour s’assurer qu’il ne fait pas la division entière

Select 
   Grade, Count(ID) * 100.0 / ((Select Count(ID) From MyTable) * 1.0)
From MyTable
Group By Grade
5
GordyII

Je pense que c'est une solution générale, même si je l'ai testée avec IBM Informix Dynamic Server 11.50.FC3. La requête suivante:

SELECT grade,
       ROUND(100.0 * grade_sum / (SELECT COUNT(*) FROM grades), 2) AS pct_of_grades
    FROM (SELECT grade, COUNT(*) AS grade_sum
            FROM grades
            GROUP BY grade
         )
    ORDER BY grade;

donne le résultat suivant sur les données de test affichées sous la règle horizontale. La fonction ROUND peut être spécifique au SGBD, mais le reste (probablement) ne l’est pas. (Notez que j'ai changé 100 à 100.0 pour m'assurer que le calcul s'effectue avec une arithmétique non-entière - DECIMAL, NUMERIC - voir les commentaires et grâce à Thunder.)

grade  pct_of_grades
CHAR(1) DECIMAL(32,2)
A       32.26
B       16.13
C       12.90
D       12.90
E       9.68
F       16.13

CREATE TABLE grades
(
    id VARCHAR(10) NOT NULL,
    grade CHAR(1) NOT NULL CHECK (grade MATCHES '[ABCDEF]')
);

INSERT INTO grades VALUES('1001', 'A');
INSERT INTO grades VALUES('1002', 'B');
INSERT INTO grades VALUES('1003', 'F');
INSERT INTO grades VALUES('1004', 'C');
INSERT INTO grades VALUES('1005', 'D');
INSERT INTO grades VALUES('1006', 'A');
INSERT INTO grades VALUES('1007', 'F');
INSERT INTO grades VALUES('1008', 'C');
INSERT INTO grades VALUES('1009', 'A');
INSERT INTO grades VALUES('1010', 'E');
INSERT INTO grades VALUES('1001', 'A');
INSERT INTO grades VALUES('1012', 'F');
INSERT INTO grades VALUES('1013', 'D');
INSERT INTO grades VALUES('1014', 'B');
INSERT INTO grades VALUES('1015', 'E');
INSERT INTO grades VALUES('1016', 'A');
INSERT INTO grades VALUES('1017', 'F');
INSERT INTO grades VALUES('1018', 'B');
INSERT INTO grades VALUES('1019', 'C');
INSERT INTO grades VALUES('1020', 'A');
INSERT INTO grades VALUES('1021', 'A');
INSERT INTO grades VALUES('1022', 'E');
INSERT INTO grades VALUES('1023', 'D');
INSERT INTO grades VALUES('1024', 'B');
INSERT INTO grades VALUES('1025', 'A');
INSERT INTO grades VALUES('1026', 'A');
INSERT INTO grades VALUES('1027', 'D');
INSERT INTO grades VALUES('1028', 'B');
INSERT INTO grades VALUES('1029', 'A');
INSERT INTO grades VALUES('1030', 'C');
INSERT INTO grades VALUES('1031', 'F');
5
Jonathan Leffler
SELECT Grade, GradeCount / SUM(GradeCount)
FROM (SELECT Grade, COUNT(*) As GradeCount
      FROM myTable
      GROUP BY Grade) Grades
4
Aakashi

Dans toute version de serveur SQL, vous pouvez utiliser une variable pour le total de toutes les notes, comme ceci:

declare @countOfAll decimal(18, 4)
select @countOfAll = COUNT(*) from Grades

select
Grade,  COUNT(*) / @countOfAll * 100
from Grades
group by Grade
3
Steve Willcock

Vous pouvez utiliser une sous-sélection dans votre requête from (non testée et ne sachant pas laquelle est la plus rapide):

SELECT Grade, COUNT(*) / TotalRows
FROM (SELECT Grade, COUNT(*) As TotalRows
      FROM myTable) Grades
GROUP BY Grade, TotalRows

Ou

SELECT Grade, SUM(PartialCount)
FROM (SELECT Grade, 1/COUNT(*) AS PartialCount
      FROM myTable) Grades
GROUP BY Grade

Ou

SELECT Grade, GradeCount / SUM(GradeCount)
FROM (SELECT Grade, COUNT(*) As GradeCount
      FROM myTable
      GROUP BY Grade) Grades

Vous pouvez également utiliser une procédure stockée (excuses pour la syntaxe Firebird):

SELECT COUNT(*)
FROM myTable
INTO :TotalCount;

FOR SELECT Grade, COUNT(*)
FROM myTable
GROUP BY Grade
INTO :Grade, :GradeCount
DO
BEGIN
    Percent = :GradeCount / :TotalCount;
    SUSPEND;
END
3
lc.