SQL 문으로 백분율을 계산하는 방법


177

사용자 및 등급이 포함 된 SQL Server 테이블이 있습니다. 간단히하기 위해, 2 개의 열이 있다고 가정 해 봅시다- name& grade. 따라서 일반적인 행은 이름 : "John Doe", Grade : "A"입니다.

가능한 모든 답변의 백분율을 찾을 수있는 하나의 SQL 문을 찾고 있습니다. (A, B, C 등 ...) 또한 가능한 모든 대답을 정의하지 않고이 작업을 수행 할 수있는 방법이 있습니까 (열린 텍스트 필드-사용자는 '통과 / 실패', '없음'등을 입력 할 수 있습니다 ...)

내가 찾고있는 최종 출력은 A : 5 %, B : 15 %, C : 40 % 등입니다.

답변:


227

나는 다음을 테스트했으며 이것이 효과가 있습니다. gordyii의 대답은 가까웠지만 잘못된 위치에 100의 곱셈이 있었고 괄호가 누락되었습니다.

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

21
결과는 정수로 나타납니다. 결과의 합이 100과 같지 않습니다.
Thunder

10
테이블이 두 번 스캔되므로 가장 효율적이지 않습니다. 또한 참조 된 테이블이 두 개 이상인 경우 쿼리가 그렇게 단순 해 보이지 않습니다.
Alex Aza

14
@Thunder 값으로 100을 100.0으로 변경할 수 있습니다.
Joseph

누군가 SQL 쿼리의 수학적 구문이 일반적으로 기대하지 않는 이유를 설명 할 수 있습니까? 예를 들어 정상 나는 총 시간으로 나눈 다음 100으로 나 have 것입니까? 논리적 관점에서 이것에 대해 진정으로 궁금합니다.
Digitalsa1nt

4
@ Digitalsa1nt (100 * 2) / 4 = 50, (2/4) * 100 = 50은 열거자가 곱해지는 부분입니다. SQL 문의 우선 순위로 인해 동일합니다. 그러나 100을 사용하는 경우 데이터 유형으로 인해 %에 대해 원하는 소수점 이하 자릿수로 반올림 할 수 있습니다. 분할 연산 후에 넣는 것처럼 처리 할 수있는 데이터 유형으로 캐스팅해야합니다. 소수점 이하 자릿수는 100 또는 0으로 끝나고 실제 백분율은 아닙니다.
Matt

232
  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 G.

@RJB 10 진수로 출력을 캐스팅 할 때 왜 100이 아니라 100.0을 곱해야합니까?
AS91

2
나누기 작업 후에 10 진수로 캐스트되기 때문에 @ AS91. int (100)를 그대로두면 다른 int로 나누면 int도 생겨 결과가 반올림됩니다. 그렇기 때문에 트릭은 항상 실제 나누기 전에 배당금에 대한 캐스트를 강제하는 것입니다 (1.0과 같은 리터럴 10 진수를 곱하거나 캐스트 / 변환 할 수 있음)
luiggig

와 옵션 1 over()PostgreSQL을 10 작품의 큰
제임스 데일리

40

총계를 얻기 위해 별도의 CTE를 사용하는 대신 "partition by"절없이 창 함수를 사용할 수 있습니다.

사용중인 경우 :

count(*)

그룹의 수를 얻으려면 다음을 사용할 수 있습니다.

sum(count(*)) over ()

총 수를 얻을 수 있습니다.

예를 들면 다음과 같습니다.

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

내 경험상 더 빠르지는 않지만 내부적으로 임시 테이블을 사용하는 경우가 있다고 생각합니다 ( "set statistics io on"으로 실행할 때 "작업 테이블"을 보았습니다).

편집 : 내 예제 쿼리가 당신이 찾고있는 것인지 확실하지 않습니다. 윈도우 기능이 어떻게 작동하는지 보여줍니다.


+1. 대단하다. 'table'대신 select 문이있는 경우에도 사용할 수 있습니다.
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.
Jonathan Leffler

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)')가 있으므로 그룹별로 절이 필요하지 않습니까? 그리고 표준 SQL에서는 '/ (SELECT COUNT (*) FROM Grades)'를 사용하여 총합계를 얻을 수 있다고 생각합니다.
Jonathan Leffler

IBM Informix Dynamic Server는 선택 목록의 기본 SUM을 좋아하지 않습니다 (고소 할 때 다소 도움이되지 않는 메시지를 표시 함). 내 대답과 이전 의견에서 언급했듯이 select-list에서 전체 하위 선택 식을 사용하면 IDS에서 작동합니다.
Jonathan Leffler

내부 쿼리에 복잡한 위치를 적용 할 수 있기 때문에 더 좋습니다.
mvmn 2016

9

백분율을 계산해야 할 때만 사용합니다.

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

100.0은 소수를 반환하지만 100 자체는 ROUND () 함수를 사용하더라도 결과를 가장 가까운 정수로 반올림합니다!


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으로 돌아옵니다. 적절한 답변을 보려면 일종의 숫자 서식 또는 변환을 수행해야합니까?
Alex

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으로 변경했습니다. 주석을 참조하십시오. 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으로 바꾸면 어떻게 되나요?
Jonathan Leffler

결과는 100.0으로 소수입니다.
Thunder

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

3

모든 SQL Server 버전에서 다음과 같이 모든 등급의 변수를 사용할 수 있습니다.

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

나는 이것과 비슷한 문제가 있었다. 올바른 결과에 100 대신 1.0을 곱한 결과를 얻을 수 있어야합니다.

학년별 MyTable 그룹 점수로 (등급 (등급) * 1.0 / (MyTable에서 개수 (*) 선택))을 선택합니다. 첨부 된 참조 이미지 참조


꼭 필요한 경우가 아니면 정보를 이미지로 공유하지 마십시오. meta.stackoverflow.com/questions/303812/…를 참조하십시오 .
AMC

0

이것은 MS SQL에서 잘 작동합니다. varchar를 2 진수로 제한된 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;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.