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

Alex
sumber

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
Jason
sumber
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;
Alex Aza
sumber
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.

John Gibb
sumber
+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
Jhonny D. Cano -Leftware-
sumber
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.

Jeremy
sumber
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 ()!

Fandango68
sumber
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
GordyII
sumber
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');
Jonathan Leffler
sumber
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
Aakashi
sumber
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
Steve Willcock
sumber
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
lc.
sumber
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

gabriel egbenya
sumber
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;
Kokokoko
sumber