Como calcular porcentagem com uma instrução SQL

Eu tenho uma tabela do SQL Server que contém usuários e suas notas. Para simplificar, vamos apenas dizer que existem duas colunas – name e grade . Assim, uma linha típica seria Nome: “John Doe”, Grade: “A”.

Estou procurando uma instrução SQL que encontre as porcentagens de todas as respostas possíveis. (A, B, C, etc …) Além disso, existe uma maneira de fazer isso sem definir todas as respostas possíveis (campo de texto aberto – os usuários podem digitar ‘passar / falhar’, ‘nenhum’, etc …)

A saída final que estou procurando é A: 5%, B: 15%, C: 40%, etc …

Eu testei o seguinte e isso funciona. A resposta de gordyii foi próxima, mas teve a multiplicação de 100 no lugar errado e alguns parênteses faltantes.

 Select Grade, (Count(Grade)* 100 / (Select Count(*) From MyTable)) as Score From MyTable Group By Grade 
  1. O mais eficiente (usando over ()).

     select Grade, count(*) * 100.0 / sum(count(*)) over() from MyTable group by Grade 
  2. Universal (qualquer versão do SQL).

     select Rate, count(*) * 100.0 / (select count(*) from MyTable) from MyTable group by Rate; 
  3. Com CTE, o menos eficiente.

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

Em vez de usar um CTE separado para obter o total, você pode usar uma function de janela sem a cláusula “partição por”.

Se você estiver usando:

 count(*) 

para obter a contagem de um grupo, você pode usar:

 sum(count(*)) over () 

para obter a contagem total.

Por exemplo:

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

Ele tende a ser mais rápido na minha experiência, mas eu acho que pode usar internamente uma tabela temporária em alguns casos (eu vi “Worktable” quando rodando com “set statistics io on”).

EDIT: Não tenho certeza se minha consulta de exemplo é o que você está procurando, eu estava apenas ilustrando como funcionam as funções de janelas.

Você tem que calcular o total de notas Se é SQL 2005 você pode usar 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 

Você precisa agrupar no campo de notas. Essa consulta deve fornecer o que você está procurando em praticamente qualquer database.

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

Você deve especificar o sistema que está usando.

Eu simplesmente uso isso sempre que preciso calcular uma porcentagem.

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

Observe que 100.0 retorna decimais, enquanto que 100 por si próprio arredondarão o resultado para o número inteiro mais próximo, mesmo com a function ROUND ()!

Esta é, acredito, uma solução geral, embora eu tenha testado usando o IBM Informix Dynamic Server 11.50.FC3. A consulta a seguir:

 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; 

fornece a seguinte saída nos dados de teste mostrados abaixo da regra horizontal. A function ROUND pode ser específica do DBMS, mas o restante (provavelmente) não é. (Note que eu mudei 100 para 100.0 para garantir que o cálculo ocorra usando não-inteiro – DECIMAL, NUMERIC – aritmética; veja os comentários, e obrigado ao 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'); 

O seguinte deve funcionar

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

EDIT: movido o * 100 e adicionado o 1.0 para garantir que ele não faz divisão inteira

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

Em qualquer versão do servidor sql você poderia usar uma variável para o total de todas as notas como esta:

 declare @countOfAll decimal(18, 4) select @countOfAll = COUNT(*) from Grades select Grade, COUNT(*) / @countOfAll * 100 from Grades group by Grade 

Você pode usar uma subseleção na sua consulta (não testada e sem certeza que é mais rápida):

 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 

Você também pode usar um procedimento armazenado (desculpas pela syntax do 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 
 SELECT Grade, GradeCount / SUM(GradeCount) FROM (SELECT Grade, COUNT(*) As GradeCount FROM myTable GROUP BY Grade) Grades