如何使用 SQL 语句计算百分比
Posted
技术标签:
【中文标题】如何使用 SQL 语句计算百分比【英文标题】:How to calculate percentage with a SQL statement 【发布时间】:2010-10-20 17:06:01 【问题描述】:我有一个包含用户及其成绩的 SQL Server 表。为简单起见,假设有 2 列 - name
和 grade
。因此,典型的行将是名称:“John Doe”,等级:“A”。
我正在寻找一个 SQL 语句,它可以找到所有可能答案的百分比。 (A、B、C 等......)另外,有没有办法在不定义所有可能答案的情况下做到这一点(打开文本字段 - 用户可以输入“通过/失败”、“无”等......)
我正在寻找的最终输出是 A: 5%, B: 15%, C: 40%, 等等...
【问题讨论】:
【参考方案1】:最高效的(使用over())。
select Grade, count(*) * 100.0 / sum(count(*)) over()
from MyTable
group by Grade
通用(任何 SQL 版本)。
select Grade, count(*) * 100.0 / (select count(*) from MyTable)
from MyTable
group by Grade;
使用 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;
【讨论】:
over() 在我的 SQL Server 2008 上完美运行,我做了数学确认。为了将其四舍五入到小数点后 2 位,我使用了 CAST(count() * 100.0 / sum(count()) over() AS DECIMAL(18, 2))。感谢您的帖子! 如果您在 100 乘法上溢出(例如 将表达式转换为数据类型 int 的算术溢出错误),请将其替换为分母中的除法:cast((count(*) / (sum(count(*)) over() / 100)) AS DECIMAL(18, 2)) as Percentage
@RJB 为什么在将输出转换为小数时必须乘以 100.0 而不仅仅是 100?
@AS91,因为转换为小数发生在除法操作之后。如果你留下一个 int (100),除以另一个 int 也会得到一个 int,这将对结果进行四舍五入。这就是为什么诀窍总是在实际除法之前强制对被除数进行强制转换(您可以乘以像 1.0 这样的文字小数或强制转换/转换)
选项 1 和 over()
在 Postgresql 10 上效果很好【参考方案2】:
我已经测试了以下内容,这确实有效。 gordyii 的答案很接近,但乘以 100 的位置错误,并且缺少括号。
Select Grade, (Count(Grade)* 100 / (Select Count(*) From MyTable)) as Score
From MyTable
Group By Grade
【讨论】:
这给出了整数的结果。结果的总和不等于 100。 不是最有效的,因为表格将被扫描两次。此外,如果引用了多个表,查询看起来也不会那么简单。 @Thunder 您可以将十进制值从 100 更改为 100.0。 有人能解释一下为什么 SQL 查询的数学语法不是您通常期望的那样吗?例如正常我会除以总数然后乘以100?从逻辑的角度来看,我真的对此感到好奇。 @Digitalsa1nt (100 * 2) / 4 = 50, (2/4) * 100 = 50 只要枚举数是被相乘的部分。由于 SQL 语句的优先级,它将是相同的。但是,由于数据类型,如果使用 100,您仍然可以将结果四舍五入到小数点后 0,就像您将它放在除法运算之后一样,您必须确保转换为可以处理的数据类型小数位,否则你最终会得到 100 或 0,而不是实际的百分比【参考方案3】:您可以使用不带“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”,也可以使用它。 它在工作台tempdb
中使用了一个线轴。逻辑读取似乎更高but they are counted differently than normal
实际上,查询中的COUNT(*) OVER ()
会返回一个完全不相关的数字(具体来说,是分组 结果集的行数)。你应该改用SUM(COUNT(*)) OVER ()
。【参考方案4】:
我只是在需要计算百分比时使用它..
ROUND(CAST((Numerator * 100.0 / Denominator) AS FLOAT), 2) AS Percentage
请注意,100.0 返回 1 个小数,而 100 本身会将结果四舍五入到最接近的整数,即使使用 ROUND(...,2) 函数也是如此!
【讨论】:
【参考方案5】:您必须计算总成绩 如果是 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。 对我来说隐藏的宝石是你注释掉了 CONVERT。【参考方案6】:您需要在成绩字段上进行分组。这个查询应该给你你在几乎任何数据库中寻找的东西。
Select Grade, CountofGrade / sum(CountofGrade) *100
from
(
Select Grade, Count(*) as CountofGrade
From Grades
Group By Grade) as sub
Group by Grade
您应该指定您正在使用的系统。
【讨论】:
既然你在外部选择中有一个聚合 ('sum(CountofGrade)'),你不需要一个 group by 子句吗?在标准 SQL 中,我认为您可以使用 '/ (SELECT COUNT(*) FROM Grades)' 来获得总计。 IBM Informix Dynamic Server 不喜欢选择列表中的裸 SUM(尽管它在抱怨时给出了一些不太有用的消息)。正如我的回答和之前的评论中所指出的,在选择列表中使用完整的子选择表达式在 IDS 中确实有效。 这也更好,因为可以将复杂的 where 应用于内部查询。【参考方案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
【讨论】:
这行得通,但答案都返回为 0 - 我是否需要进行某种数字格式化或转换才能看到正确的答案? Select Grade, round(Count(grade) * 100.0 / ((Select Count(grade) From grades) * 1.0) ,2) From grades Group By Grade在sql-server中添加一个round函数返回例如:21.56000000000【参考方案8】:我相信这是一个通用的解决方案,尽管我使用 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 - 算术进行计算;请参阅 cmets,感谢 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【参考方案9】:SELECT Grade, GradeCount / SUM(GradeCount)
FROM (SELECT Grade, COUNT(*) As GradeCount
FROM myTable
GROUP BY Grade) Grades
【讨论】:
【参考方案10】:在任何 sql server 版本中,您都可以使用一个变量来计算所有成绩的总和,如下所示:
declare @countOfAll decimal(18, 4)
select @countOfAll = COUNT(*) from Grades
select
Grade, COUNT(*) / @countOfAll * 100
from Grades
group by Grade
【讨论】:
【参考方案11】:您可以在 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
【讨论】:
【参考方案12】:我遇到了类似的问题。您应该能够得到乘以 1.0 而不是 100 的正确结果。请参阅附加的示例图片
Select Grade, (Count(Grade)* 1.0 / (Select Count(*) From MyTable)) as Score From MyTable Group By Grade
【讨论】:
除非绝对必要,否则请不要将信息共享为图像。请参阅:meta.***.com/questions/303812/…。【参考方案13】:这个在 MS SQL 中运行良好。它将 varchar 转换为两位小数限制浮点数的结果。
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;
【讨论】:
以上是关于如何使用 SQL 语句计算百分比的主要内容,如果未能解决你的问题,请参考以下文章