Cara menghitung persentase dengan pernyataan SQL


177

Saya memiliki tabel SQL Server yang berisi pengguna & nilainya. Demi kesederhanaan, katakan saja ada 2 kolom - name& grade. Jadi baris yang khas adalah Name: "John Doe", Grade: "A".

Saya mencari satu pernyataan SQL yang akan menemukan persentase dari semua jawaban yang mungkin. (A, B, C, dll ...) Juga, apakah ada cara untuk melakukan ini tanpa mendefinisikan semua kemungkinan jawaban (bidang teks terbuka - pengguna dapat memasukkan 'lulus / gagal', 'tidak ada', dll ...)

Output akhir yang saya cari adalah A: 5%, B: 15%, C: 40%, dll ...

Jawaban:


227

Saya telah menguji yang berikut ini dan ini berhasil. Jawaban oleh gordyii dekat tetapi memiliki kelipatan 100 di tempat yang salah dan memiliki beberapa tanda kurung yang hilang.

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

21
ini memberikan hasil dalam bilangan bulat .jumlah hasil tidak sama dengan 100.
Thunder

10
Bukan yang paling efisien karena tabel akan dipindai dua kali. Juga kueri tidak akan terlihat sesederhana itu jika ada lebih dari satu tabel yang direferensikan.
Alex Aza

14
@Pikir, Anda dapat mengubah 100 hingga 100.0 untuk nilai desimal.
Joseph

Adakah yang bisa menjelaskan mengapa sintaksis matematis kueri SQL tidak seperti yang Anda harapkan secara normal? Sebagai contoh, normal saya akan dibagi dengan total maka kali dengan 100? Sangat ingin tahu tentang ini dari sudut pandang logis.
Digitalsa1nt

4
@ Digitalsa1nt (100 * 2) / 4 = 50, (2/4) * 100 = 50 selama enumerator adalah bagian yang dikalikan. Karena diutamakan pernyataan SQL itu akan sama. Namun, karena tipe data jika menggunakan 100 Anda masih bisa mendapatkan hasil dibulatkan menjadi 0 desimal yang Anda inginkan untuk% di mana seolah-olah Anda meletakkannya setelah operasi pembagian Anda harus memastikan bahwa Anda melemparkan ke tipe data yang dapat menangani tempat desimal jika tidak, Anda akan berakhir dengan 100 atau 0 dan tidak pernah persentase yang sebenarnya
Matt

231
  1. Yang paling efisien (menggunakan lebih ()).

    select Grade, count(*) * 100.0 / sum(count(*)) over()
    from MyTable
    group by Grade
  2. Universal (semua versi SQL).

    select Grade, count(*) * 100.0 / (select count(*) from MyTable)
    from MyTable
    group by Grade;
  3. Dengan CTE, yang paling tidak efisien.

    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 () bekerja dengan sempurna pada SQL Server 2008 saya, saya melakukan matematika untuk mengonfirmasi. Untuk membulatkannya ke 2 tempat desimal saya menggunakan CAST (count ( ) * 100.0 / sum (count ( )) over () AS DECIMAL (18, 2)). Terima kasih untuk kirimannya!
RJB

3
Jika Anda overflow pada perkalian 100 (mis. Arithmetic overflow error mengubah ekspresi ke tipe data int ), ganti dengan divisi dalam penyebut sebagai gantinya:cast((count(*) / (sum(count(*)) over() / 100)) AS DECIMAL(18, 2)) as Percentage
Nikita G.

@ RJB Mengapa Anda harus mengalikan dengan 100.0 dan bukan hanya 100 ketika Anda mencetak output sebagai desimal?
AS91

2
@ AS91, karena gips ke desimal terjadi SETELAH operasi pembagian. Jika Anda meninggalkan int (100), membaginya dengan int lain akan menghasilkan int juga, yang akan membulatkan hasilnya. Itulah sebabnya triknya adalah selalu memaksa pemain pada dividen sebelum pembagian yang sebenarnya (Anda dapat mengalikan dengan desimal literal seperti 1.0 atau pemain / konversi)
luiggig

Opsi 1 dengan over()karya hebat di Postgresql 10
James Daily

40

Alih-alih menggunakan CTE terpisah untuk mendapatkan total, Anda dapat menggunakan fungsi jendela tanpa klausa "partisi dengan".

Jika Anda menggunakan:

count(*)

untuk mendapatkan hitungan untuk grup, Anda dapat menggunakan:

sum(count(*)) over ()

untuk mendapatkan jumlah total.

Sebagai contoh:

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

Ini cenderung lebih cepat dalam pengalaman saya, tetapi saya pikir itu mungkin secara internal menggunakan tabel temp dalam beberapa kasus (saya telah melihat "Meja Kerja" ketika menjalankan dengan "set statistik io on").

EDIT: Saya tidak yakin apakah contoh permintaan saya adalah apa yang Anda cari, saya baru saja menggambarkan cara kerja fungsi windowing.


+1. Ini bagus. Ini juga dapat digunakan jika di tempat 'tabel' ada pernyataan pilih.
mr_georg

1
Ini menggunakan kumparan di tempdbmana adalah tabel kerja. Bacaan logis tampaknya lebih tinggi tetapi mereka dihitung secara berbeda dari biasanya
Martin Smith

1
Sebenarnya, COUNT(*) OVER ()dalam kueri Anda akan mengembalikan angka yang sama sekali tidak terkait (khususnya, jumlah baris dari set hasil yang dikelompokkan ). Anda harus menggunakannya SUM(COUNT(*)) OVER ()sebagai gantinya.
Andriy M

10

Anda harus menghitung total nilai. Jika itu SQL 2005, Anda dapat menggunakan 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
Tentu saja, ini hanya memberikan persentase untuk kode nilai yang ada dalam tabel, bukan untuk mereka yang bisa hadir dan tidak. Tetapi tanpa daftar pasti dari kode nilai yang relevan (valid), Anda tidak dapat berbuat lebih baik. Karena itu +1 dari saya.
Jonathan Leffler

1
Permata tersembunyi bagi saya adalah Anda berkomentar CONVERT.
Chris Catignani

9

Anda perlu mengelompokkan pada bidang nilai. Kueri ini akan memberi Anda apa yang Anda cari di hampir semua basis data.

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

Anda harus menentukan sistem yang Anda gunakan.


2
Karena Anda memiliki agregat ('jumlah (CountofGrade)') di pilih luar, tidakkah Anda memerlukan grup dengan klausul di dalamnya juga? Dan dalam SQL standar, saya pikir Anda bisa menggunakan '/ (SELECT COUNT (*) FROM Grades)' untuk mendapatkan total keseluruhan.
Jonathan Leffler

IBM Informix Dynamic Server tidak menyukai SUM telanjang di daftar-pilih (meskipun memberikan pesan yang agak kurang bermanfaat ketika mengeluh). Seperti disebutkan dalam jawaban dan komentar saya sebelumnya, menggunakan ekspresi sub-pilih lengkap dalam daftar pilih tidak berfungsi di IDS.
Jonathan Leffler

Ini juga lebih baik karena seseorang dapat menerapkan permintaan kompleks ke dalam.
mvmn

9

Saya hanya menggunakan ini kapan pun saya perlu menghitung persentase ..

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

Perhatikan bahwa 100.0 menghasilkan desimal, sedangkan 100 itu sendiri akan mengumpulkan hasilnya ke bilangan bulat terdekat, bahkan dengan fungsi ROUND ()!


7

Berikut ini harus bekerja

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

EDIT: Memindahkan * 100dan menambahkan 1.0untuk memastikan tidak melakukan pembagian integer

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

1
ini berfungsi, tetapi semua jawaban kembali sebagai 0 - apakah saya perlu melakukan semacam pemformatan angka atau konversi untuk melihat jawaban yang tepat?
Alex

1
Pilih Grade, round (Hitungan (grade) * 100.0 / ((Select Count (grade) Dari nilai) * 1.0), 2) Dari nilai Group By Grade untuk menambahkan fungsi bundar dalam sql-server returend misalnya: 21.56000000000
Guntur

5

Ini, saya percaya, solusi umum, meskipun saya mengujinya menggunakan IBM Informix Dynamic Server 11.50.FC3. Pertanyaan berikut:

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;

memberikan output berikut pada data uji yang ditunjukkan di bawah aturan horizontal. The ROUNDFungsi mungkin DBMS-spesifik, tapi sisa (mungkin) tidak. (Perhatikan bahwa saya mengubah 100 menjadi 100.0 untuk memastikan bahwa perhitungan terjadi menggunakan non-integer - DECIMAL, NUMERIC - aritmatika; lihat komentar, dan terima kasih kepada 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');

memberikan bilangan bulat integer dalam sql-server
Thunder

@Thunder: menarik; apa yang terjadi jika Anda mengubah, katakanlah, 100 ke 100,00?
Jonathan Leffler

Yakin hasilnya dalam desimal dengan 100,0
Thunder

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

3

Dalam versi server sql apa pun, Anda dapat menggunakan variabel untuk total semua nilai seperti ini:

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

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

3

Anda dapat menggunakan sub-pilihan dalam kueri dari Anda (belum teruji dan tidak yakin mana yang lebih cepat):

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

Atau

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

Atau

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

Anda juga dapat menggunakan prosedur tersimpan (permintaan maaf untuk sintaks 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

Saya memiliki masalah yang mirip dengan ini. Anda harus bisa mendapatkan hasil yang benar dengan mengalikan dengan 1,0 bukannya 100. Lihat contoh Gambar terlampir

Pilih Nilai, (Hitung (Nilai) * 1.0 / (Pilih Hitung (*) Dari MyTable)) sebagai Skor Dari Grup MyTable Dengan Nilai Lihat gambar referensi terlampir


Tolong jangan berbagi informasi sebagai gambar kecuali benar-benar diperlukan. Lihat: meta.stackoverflow.com/questions/303812/… .
AMC

0

Yang ini bekerja dengan baik di MS SQL. Ini mengubah varchar menjadi hasil dari float terbatas dua desimal-tempat-terbatas.

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;
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.