Cách tính tỷ lệ phần trăm với câu lệnh SQL


177

Tôi có bảng SQL Server chứa người dùng và điểm của họ. Để đơn giản, hãy giả sử có 2 cột - name& grade. Vì vậy, một hàng tiêu biểu sẽ là Tên: "John Doe", Lớp: "A".

Tôi đang tìm kiếm một câu lệnh SQL sẽ tìm thấy tỷ lệ phần trăm của tất cả các câu trả lời có thể. (A, B, C, v.v ...) Ngoài ra, có một cách để làm điều này mà không cần xác định tất cả các câu trả lời có thể (trường văn bản mở - người dùng có thể nhập 'pass / fail', 'none', v.v ...)

Sản phẩm cuối cùng tôi đang tìm kiếm là A: 5%, B: 15%, C: 40%, v.v ...

Câu trả lời:


226

Tôi đã thử nghiệm sau đây và điều này không hoạt động. Câu trả lời của gordyii rất gần nhưng có bội số 100 ở sai vị trí và có một số dấu ngoặc đơn bị thiếu.

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

21
điều này mang lại kết quả về số nguyên .sum kết quả không bằng 100.
Thunder

10
Không hiệu quả nhất vì bảng sẽ được quét hai lần. Ngoài ra, truy vấn sẽ không đơn giản nếu có nhiều hơn một bảng được tham chiếu.
Alex Aza

14
@Thunder bạn có thể thay đổi 100 thành 100.0 cho các giá trị thập phân.
Giuse

Ai đó có thể giải thích tại sao cú pháp toán học của truy vấn SQL không phải là điều bạn mong muốn làm bình thường không? Ví dụ bình thường tôi sẽ chia cho tổng số lần sau đó cho 100? Thực sự tò mò về điều này từ một quan điểm hợp lý.
Digitalsa1nt

4
@ Digitalsa1nt (100 * 2) / 4 = 50, (2/4) * 100 = 50 miễn là điều tra viên là phần được nhân lên. Do sự ưu tiên của các câu lệnh SQL, nó sẽ giống nhau. tuy nhiên, do các loại dữ liệu nếu sử dụng 100, bạn vẫn có thể nhận được kết quả được làm tròn thành 0 số thập phân mà bạn mong muốn cho% khi bạn đặt nó sau thao tác phân chia, bạn sẽ phải đảm bảo rằng bạn chuyển sang loại dữ liệu có thể xử lý vị trí thập phân nếu không bạn sẽ kết thúc bằng 100 hoặc 0 và không bao giờ là tỷ lệ phần trăm thực tế
Matt

231
  1. Hiệu quả nhất (sử dụng hơn ()).

    select Grade, count(*) * 100.0 / sum(count(*)) over()
    from MyTable
    group by Grade
  2. Phổ thông (bất kỳ phiên bản SQL nào).

    select Grade, count(*) * 100.0 / (select count(*) from MyTable)
    from MyTable
    group by Grade;
  3. Với CTE, kém hiệu quả nhất.

    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 () hoạt động hoàn hảo trên SQL Server 2008 của tôi, tôi đã làm toán để xác nhận. Để làm tròn nó đến 2 vị trí thập phân, tôi đã sử dụng CAST (Count ( ) * 100.0 / sum (Count ( )) over () AS DECIMAL (18, 2)). Cảm ơn vì bài đăng!
RJB

3
Trong trường hợp bạn tràn vào phép nhân 100 (ví dụ: lỗi tràn số học chuyển đổi biểu thức sang kiểu dữ liệu int ), thay thế nó bằng phép chia theo mẫu số thay vào đó:cast((count(*) / (sum(count(*)) over() / 100)) AS DECIMAL(18, 2)) as Percentage
Nikita G.

@RJB Tại sao bạn phải nhân 100.0 chứ không chỉ 100 khi bạn truyền đầu ra dưới dạng thập phân?
AS91

2
@ AS91, vì chuyển sang số thập phân xảy ra SAU hoạt động phân chia. Nếu bạn để một int (100), chia cho một int khác cũng sẽ dẫn đến một int, nó sẽ làm tròn kết quả. Đó là lý do tại sao mánh khóe là luôn ép buộc cổ tức trước khi chia thực tế (bạn có thể nhân với một số thập phân theo nghĩa đen như 1.0 hoặc cast / convert)
luiggig

Tùy chọn 1 với over()các tác phẩm tuyệt vời trên Postgresql 10
James Daily

40

Thay vì sử dụng một CTE riêng để lấy tổng, bạn có thể sử dụng hàm window mà không có mệnh đề "phân vùng theo".

Nếu bạn đang sử dụng:

count(*)

để lấy số lượng cho một nhóm, bạn có thể sử dụng:

sum(count(*)) over ()

để có được tổng số.

Ví dụ:

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

Nó có xu hướng nhanh hơn trong trải nghiệm của tôi, nhưng tôi nghĩ rằng nó có thể sử dụng bảng tạm thời trong một số trường hợp (tôi đã thấy "Worktable" khi chạy với "đặt số liệu thống kê io").

EDIT: Tôi không chắc chắn nếu truy vấn ví dụ của tôi là những gì bạn đang tìm kiếm, tôi chỉ minh họa cách các chức năng cửa sổ hoạt động.


+1. Điều đó thật tuyệt. Nó cũng có thể được sử dụng nếu ở vị trí 'bảng' có câu lệnh chọn.
mr_georg

1
Nó sử dụng một ống chỉ trong tempdbđó là bàn làm việc. Số lần đọc logic có vẻ cao hơn nhưng chúng được tính khác so với bình thường
Martin Smith

1
Trên thực tế, COUNT(*) OVER ()trong truy vấn của bạn sẽ trả về một con số hoàn toàn không liên quan (cụ thể là số lượng hàng của tập kết quả được nhóm ). Bạn nên sử dụng SUM(COUNT(*)) OVER ()thay thế.
Andriy M

10

Bạn phải tính tổng số điểm Nếu là SQL 2005, bạn có thể sử dụng 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
Tất nhiên, điều này chỉ đưa ra tỷ lệ phần trăm cho các mã lớp có trong bảng, không dành cho những mã có thể có và không có. Nhưng không có danh sách chính xác các mã lớp liên quan (hợp lệ), bạn không thể làm tốt hơn. Do đó +1 từ tôi.
Jonathan Leffler

1
Viên ngọc ẩn cho tôi là bạn đã nhận xét CHUYỂN ĐỔI.
Chris Catignani

9

Bạn cần nhóm trên trường lớp. Truy vấn này sẽ cung cấp cho bạn những gì bạn đang tìm kiếm trong hầu hết các cơ sở dữ liệu.

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

Bạn nên chỉ định hệ thống bạn đang sử dụng.


2
Vì bạn có một tổng hợp ('tổng (CountofGrade)') trong lựa chọn bên ngoài, bạn cũng không cần một nhóm theo mệnh đề trong đó chứ? Và trong SQL tiêu chuẩn, tôi nghĩ rằng bạn có thể sử dụng '/ (CHỌN COUNT (*) TỪ các lớp)' để có được tổng số lớn.
Jonathan Leffler

Máy chủ động IBM Informix không thích SUM trần trụi trong danh sách lựa chọn (mặc dù nó đưa ra một thông báo ít hữu ích hơn khi nó phàn nàn). Như đã lưu ý trong câu trả lời và nhận xét trước của tôi, sử dụng biểu thức chọn phụ đầy đủ trong danh sách chọn sẽ hoạt động trong IDS.
Jonathan Leffler

Điều này cũng tốt hơn bởi vì người ta có thể áp dụng phức tạp nơi truy vấn bên trong.
mvmn

9

Tôi chỉ đơn giản sử dụng cái này khi tôi cần tính ra một tỷ lệ phần trăm ..

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

Lưu ý rằng 100.0 trả về số thập phân, trong khi 100 chính nó sẽ làm tròn kết quả thành số nguyên gần nhất, ngay cả với hàm ROUND ()!


7

Sau đây nên làm việc

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

EDIT: Đã di chuyển * 100và thêm vào 1.0để đảm bảo rằng nó không thực hiện phân chia số nguyên

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

1
điều này hoạt động, nhưng tất cả các câu trả lời đều trở về 0 - tôi có cần thực hiện một số loại định dạng hoặc chuyển đổi số để xem câu trả lời thích hợp không?
Alex

1
Chọn Lớp, vòng (Đếm (lớp) * 100.0 / ((Chọn Đếm (lớp) Từ lớp) * 1.0), 2) Từ lớp Nhóm theo cấp để thêm chức năng vòng trong returend máy chủ sql, ví dụ: 21.56000000000
Thunder

5

Tôi tin rằng đây là một giải pháp chung, mặc dù tôi đã thử nghiệm nó bằng IBM Informix Dynamic Server 11.50.FC3. Truy vấn sau đây:

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;

đưa ra đầu ra sau trên dữ liệu thử nghiệm được hiển thị bên dưới quy tắc ngang. Các ROUNDchức năng có thể DBMS cụ thể, nhưng phần còn lại (có lẽ) không phải là. (Lưu ý rằng tôi đã thay đổi 100 thành 100.0 để đảm bảo rằng phép tính xảy ra bằng cách sử dụng không nguyên - DECIMAL, NUMERIC - số học; xem các nhận xét và nhờ 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');

cung cấp phần trăm số nguyên trong máy chủ sql
Thunder

@Thunder: thú vị; Điều gì xảy ra nếu bạn thay đổi, giả sử, 100 đến 100,00?
Jonathan Leffler

Chắc chắn kết quả là số thập phân với 100.0
Thunder

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

3

Trong bất kỳ phiên bản máy chủ sql nào, bạn có thể sử dụng một biến cho tổng số tất cả các lớp như thế này:

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

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

3

Bạn có thể sử dụng một mục chọn trong truy vấn của bạn (chưa được kiểm tra và không chắc cái nào nhanh hơn):

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

Hoặc là

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

Hoặc là

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

Bạn cũng có thể sử dụng một quy trình được lưu trữ (xin lỗi cho cú pháp 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

Tôi đã có một vấn đề tương tự như thế này. bạn sẽ có thể nhận được kết quả chính xác nhân với 1.0 thay vì 100. Xem ví dụ Ảnh đính kèm

Chọn Lớp, (Đếm (Lớp) * 1.0 / (Chọn Đếm (*) Từ MyTable)) làm Điểm từ Nhóm MyTable theo Lớp Xem hình ảnh tham khảo đính kèm


Xin vui lòng không chia sẻ thông tin như hình ảnh trừ khi thực sự cần thiết. Xem: meta.stackoverflow.com/questions/303812/ .
AMC

0

Cái này hoạt động tốt trong MS SQL. Nó biến đổi varchar thành kết quả của float hai giới hạn thập phân.

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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.