如何使用SQL语句计算百分比


175

我有一个包含用户及其等级的SQL Server表。为了简单起见,我们只说有两列-namegrade。因此,典型的行是名称:“ John Doe”,等级:“ A”。

我正在寻找一条SQL语句,该语句将找到所有可能答案的百分比。(A,B,C等...)此外,有没有一种方法可以定义所有可能的答案(开放文本字段-用户可以输入“通过/失败”,“无”等)。

我正在寻找的最终输出是A:5%,B:15%,C:40%等...

Answers:


224

我已经测试了以下内容,这确实可行。gordyii的答案很接近,但是在错误的位置乘以100,并且缺少括号。

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

21
这给结果的整数.SUM结果不等于100
雷霆

10
效率最高,因为该表将被扫描两次。如果引用的表不止一个,查询也不会那么简单。
亚历克斯·阿扎

14
@Thunder可以将100更改为100.0的十进制值。
约瑟夫

有人可以解释为什么SQL查询的数学语法不是您通常期望的吗?例如,正常情况下,我会将总次数除以100?从逻辑的角度来看,对此真的感到很好奇。
Digitalsa1nt

4
@ Digitalsa1nt(100 * 2)/ 4 = 50,(2/4)* 100 = 50,只要枚举数是要乘的部分。由于SQL语句的优先级相同。但是,由于数据类型的原因,如果使用100,您仍然可以将结果四舍五入为所需的%小数位数,就好像您将其放在除法运算之后一样,您必须确保转换为可以处理的数据类型小数点后的位数,否则您最终将得到100或0,而永远不会是实际百分比
Matt Matt

229
  1. 最有效的(使用over())。

    select Grade, count(*) * 100.0 / sum(count(*)) over()
    from MyTable
    group by Grade
  2. 通用(任何SQL版本)。

    select Grade, count(*) * 100.0 / (select count(*) from MyTable)
    from MyTable
    group by Grade;
  3. 使用CTE,效率最低。

    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;

13
over()在我的SQL Server 2008上运行完美,我进行了数学确认。为了将其舍入到小数点后两位,我使用了CAST(count()* 100.0 / sum(count())over()AS DECIMAL(18,2))。感谢您的帖子!
RJB

3
如果您在100乘法上溢出(例如,将表达式转换为数据类型int的算术溢出错误),请用分母中的除法代替:cast((count(*) / (sum(count(*)) over() / 100)) AS DECIMAL(18, 2)) as Percentage
Nikita

@RJB将输出强制转换为小数时,为什么必须乘以100.0而不是100?
AS91

2
@ AS91,因为转换为小数会在除法运算后发生。如果您留下一个int(100),则将另一个int除以也会得到一个int,这将对结果取整。这就是为什么诀窍始终是在实际除法之前强制对红利进行强制转换(您可以乘以1.0的文字十进制数或
强制

选项1 over()可以在Postgresql 10上很好地工作
James Daily

40

您可以使用不带“ partition by”子句的窗口函数来代替使用单独的CTE来获取总数。

如果您正在使用:

count(*)

要获取组的计数,可以使用:

sum(count(*)) over ()

得到总数。

例如:

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

根据我的经验,它通常会更快,但是我认为在某些情况下它可能会在内部使用临时表(在“ set statistics io on”下运行时,我已经看到“ Worktable”)。

编辑: 我不确定我的示例查询是否是您要寻找的,我只是在说明开窗函数的工作方式。


+1。这很棒。如果有一个select语句代替“ table”,也可以使用它。
mr_georg

1
它使用tempdb工作台所在的线轴。逻辑读值似乎更高,但计算方式却与正常情况不同
Martin Smith

1
实际上,COUNT(*) OVER ()查询中的会返回一个完全不相关的数字(特别是分组结果集的行数)。您应该SUM(COUNT(*)) OVER ()改用。
Andriy M

10

您必须计算成绩的总和。如果是SQL 2005,则可以使用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
当然,这仅给出表中存在的成绩代码的百分比,而不给出可能存在和不存在的代码的百分比。但是,如果没有明确的相关(有效)等级代码列表,您将做得更好。因此,我的+1。
乔纳森·莱夫勒

1
对我而言,隐藏的宝石是您将CONVERT注释掉了。
克里斯·卡蒂尼亚尼

9

您需要在成绩字段上分组。该查询应该给您几乎在任何数据库中寻找的东西。

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

您应该指定您正在使用的系统。


2
由于外部选择中具有聚合(“ sum(CountofGrade)”),因此您是否也不需要group by子句?在标准SQL中,我认为您可以使用'/(SELECT COUNT(*)FROM Grades)'来获得总计。
乔纳森·莱夫勒

IBM Informix Dynamic Server不喜欢选择列表中的裸露SUM(尽管它在抱怨时给出的信息略有帮助)。如我的回答和先前的评论中所述,在选择列表中使用完整的子选择表达式确实可以在IDS中使用。
乔纳森·莱夫勒

这也更好,因为可以将复杂的位置应用于内部查询。
mvmn

9

每当我需要计算百分比时,我都会简单地使用它。

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

请注意,即使使用ROUND()函数,100.0也会返回小数,而单独使用100会将结果四舍五入到最接近的整数!


7

以下应该工作

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

编辑:移动* 100并添加了1.0以确保它不进行整数除法

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

1
这可行,但是答案全部返回为0-我是否需要进行某种数字格式化或转换才能看到正确的答案?
亚历克斯

1
选择等级,回合(计数(等级)* 100.0 /(((从等级中选择计数(等级))* 1.0),2)从等级按等级分组以在sql-server returend中添加舍入函数,例如:21.56000000000
Thunder

5

我相信这是一个通用的解决方案,尽管我使用IBM Informix Dynamic Server 11.50.FC3对其进行了测试。以下查询:

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;

在水平线下方显示的测试数据上提供以下输出。该ROUND功能可能是特定于DBMS的,而其余功能(可能)不是。(请注意,我将100更改为100.0,以确保使用非整数-DECIMAL,NUMERIC-算术进行计算;请参见注释,并感谢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');

给出sql-server中的整数百分比
雷霆

@Thunder:有趣;如果将100更改为100.00,会发生什么?
乔纳森·莱夫勒

确保结果为100.0的十进制数
雷霆

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

3

在任何sql服务器版本中,您都可以将变量用于所有等级的总和,如下所示:

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

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

3

您可以在from查询中使用子选择(未经测试,不确定哪个更快):

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

要么

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

要么

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

您还可以使用存储过程(对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


0

这在MS SQL中运行良好。它将varchar转换为两个小数位数限制的float的结果。

Select field1, cast(Try_convert(float,(Count(field2)* 100) / 
Try_convert(float, (Select Count(*) From table1))) as decimal(10,2)) as new_field_name 
From table1 
Group By field1, field2;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.