SQL ifadesi ile yüzde nasıl hesaplanır


177

Kullanıcıları ve notlarını içeren bir SQL Server tablo var. Basitçe söylemek gerekirse, 2 sütun olduğunu söyleyelim - name& grade. Tipik bir satır Adı: "John Doe", Sınıf: "A" olacaktır.

Tüm olası cevapların yüzdelerini bulacak bir SQL deyimi arıyorum. (A, B, C, vb ...) Ayrıca, tüm olası cevapları tanımlamadan bunu yapmanın bir yolu var mı (açık metin alanı - kullanıcılar 'başarılı / başarısız', 'hiçbiri' vb. Girebilir ...)

Aradığım son çıktı A:% 5, B:% 15, C:% 40, vb.

Yanıtlar:


227

Aşağıdakileri test ettim ve bu işe yarıyor. Gordyii'nin cevabı yakındı ama yanlış yerde 100 çarpımı vardı ve bazı parantezleri eksikti.

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

21
sonuç tamsayılar verir. sonuçların toplamı 100'e eşit değildir.
Thunder

10
Tablo iki kez taranacağı için en verimli değil. Ayrıca başvuru birden fazla tablo varsa sorgu bu kadar basit görünmez.
Alex Aza

14
@Thunder, ondalık değerler için 100 ila 100.0 arasında değiştirebilirsiniz.
Joseph

Birisi neden SQL sorgusunun matematiksel sözdiziminin normalden beklediğiniz gibi olmadığını açıklayabilir mi? Mesela normalde toplamı 100'e, sonra 100'e bölünür müydüm? Bunu mantıklı bir bakış açısından gerçekten merak ediyorum.
Digitalsa1nt

4
@ Sayım cihazı (100 * 2) / 4 = 50, (2/4) * 100 = 50, numaralandırıcı olduğu sürece çarpılan kısımdır. SQL ifadelerinin önceliği nedeniyle aynı olacaktır. Bununla birlikte, 100 kullanıldığında veri türleri nedeniyle, yine de sonuç için% 0 için istediğiniz 0 ondalığa yuvarlanırsınız; burada bölme işleminden sonra koyarsanız, işleyebilecek bir veri türüne yayın yaptığınızdan emin olmanız gerekir. ondalık basamaklar aksi halde 100 veya 0 ile sonuçlanır ve asla gerçek bir yüzde olmaz
Matt

232
  1. En verimli (over () kullanarak).

    select Grade, count(*) * 100.0 / sum(count(*)) over()
    from MyTable
    group by Grade
  2. Evrensel (herhangi bir SQL sürümü).

    select Grade, count(*) * 100.0 / (select count(*) from MyTable)
    from MyTable
    group by Grade;
  3. CTE ile en az verimlidir.

    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 () benim SQL Server 2008 üzerinde mükemmel çalıştı, onaylamak için matematik yaptım. 2 ondalık basamağa yuvarlamak için CAST (count ( ) * 100.0 / sum (count ( )) yerine () AS DECIMAL (18, 2)) kullandım. Gönderi için teşekkürler!
RJB

3
100 çarpımında taşma durumunda (örn. İfadeyi veri türü int'e dönüştüren aritmetik taşma hatası ), bunu cast((count(*) / (sum(count(*)) over() / 100)) AS DECIMAL(18, 2)) as Percentage
Nikita G.

@RJB Çıkışı ondalık sayı olarak yayınlarken neden yalnızca 100 ile değil 100.0 ile çarpmanız gerekiyor?
AS91

2
@ AS91, çünkü ondalık işleme kademesi bölme işleminden SONRA gerçekleşir. Bir int (100) bırakırsanız, başka bir int ile bölmek de sonucu yuvarlayacak bir int ile sonuçlanır. Bu yüzden hile her zaman gerçek bölünmeden önce temettü üzerinde bir oyuncu kadrosunu zorlamaktır (1.0 gibi bir değişmez ondalık sayı ile çarpabilir veya cast / convert)
luiggig

Seçenek 1 ile over()Postgresql 10
James Daily

40

Toplamı elde etmek için ayrı bir CTE kullanmak yerine, "partition by" yan tümcesi olmadan bir pencere işlevi kullanabilirsiniz.

Kullanıyorsanız:

count(*)

bir grubun sayısını almak için şunları kullanabilirsiniz:

sum(count(*)) over ()

toplam sayıyı elde etmek için.

Örneğin:

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

Deneyimlerimde daha hızlı olma eğilimindedir, ancak bazı durumlarda dahili olarak geçici bir tablo kullanabileceğini düşünüyorum ("set istatio io on" ile çalışırken "Worktable" ı gördüm).

EDIT: Örnek sorgu benim aradığınız şey olduğundan emin değilim, ben sadece pencereleme işlevlerinin nasıl çalıştığını göstermek oldu.


+1. Bu harika. 'Table' yerine bir select deyimi varsa da kullanılabilir.
mr_georg

1
İçinde tempdbçalışma masası olan bir makara kullanır . Mantıksal okumalar daha yüksek görünüyor ancak normalden farklı sayılıyor
Martin Smith

1
Aslında, COUNT(*) OVER ()sorgunuzda tamamen alakasız bir rakam (özellikle, gruplandırılmış sonuç kümesinin satır sayısı) döndürülür . Bunun SUM(COUNT(*)) OVER ()yerine kullanmalısınız .
Andriy M

10

Toplam notu hesaplamanız gerekiyor SQL 2005 ise CTE kullanabilirsiniz

    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
Tabii ki, bu sadece mevcut olan ve olmayanlar için değil, tabloda bulunan sınıf kodları için yüzdeleri verir. Ancak ilgili (geçerli) sınıf kodlarının kesin bir listesi olmadan daha iyisini yapamazsınız. Dolayısıyla benden +1.
Jonathan Leffler

1
Benim için gizli mücevher CONVERT yorumladı oldu.
Chris Catignani

9

Not alanında gruplandırmanız gerekir. Bu sorgu size hemen hemen her veritabanında aradığınızı verecektir.

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

Kullandığınız sistemi belirtmelisiniz.


2
Dış seçimde bir toplama ('sum (CountofGrade)') bulunduğundan, içinde de bir cümle grubuna ihtiyacınız yok mu? Ve standart SQL'de, genel toplamı almak için '/ (SELECT COUNT (*) FROM Grades)') kullanabilirsiniz.
Jonathan Leffler

IBM Informix Dynamic Server, seçim listesindeki çıplak TOPLA'yı sevmez (ancak şikayet ettiğinde yardımcı olmaktan biraz daha az mesaj verir). Cevabımda ve önceki yorumumda belirtildiği gibi, seçim listesinde tam bir alt seçim ifadesi kullanmak IDS'de çalışıyor.
Jonathan Leffler

Bu da daha iyidir çünkü iç sorgulamaya karmaşık bir yer uygulanabilir.
mvmn

9

Ben sadece bir yüzde çalışmak gerektiğinde bunu kullanın ..

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

100.0'ın ondalık sayılar döndürdüğünü, ancak 100'ün tek başına sonucu ROUND () işleviyle bile en yakın tam sayıya yuvarlayacağını unutmayın!


7

Aşağıdakiler işe yaramalıdır

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

DÜZENLEME: Taşındı * 100ve 1.0tamsayı bölme yapmadığından emin olmak için

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

1
Bu işe yarıyor, ancak cevapların hepsi 0 olarak geri geliyor - doğru cevabı görmek için bir çeşit sayı biçimlendirme veya dönüşüm yapmam gerekir mi?
Alex

1
Sınıf, yuvarlak (Sayım (derece) * 100.0 / ((Sınıflardan Sayım (derece) seçin) * 1.0), 2) sql-server returend'inde yuvarlak fonksiyon eklemek için Notlardan Gruplara Göre Sınıflandırma Örneği: 21.56000000000
Thunder

5

Bu, genel bir çözüm olduğuna inanıyorum, ancak IBM Informix Dynamic Server 11.50.FC3 kullanarak test ettim. Aşağıdaki sorgu:

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;

yatay kuralın altında gösterilen test verisinde aşağıdaki çıktıyı verir. ROUNDFonksiyon DBMS özgü olabilir, ama geri kalanı (muhtemelen) değildir. (Hesaplamanın tam sayı olmayan - DECIMAL, NUMERIC - aritmetik kullanarak gerçekleştiğinden emin olmak için 100'ü 100.0 olarak değiştirdiğimi unutmayın; yorumları görün ve Thunder sayesinde.)

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 tamsayı yüzde verir
Thunder

@ Gök gürültüsü: ilginç; 100'ü 100,00 olarak değiştirirseniz ne olur?
Jonathan Leffler

Elbette sonuç ondalık olarak 100.0
Thunder

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

3

Herhangi bir sql sunucusu sürümünde, aşağıdaki gibi tüm notların toplamı için bir değişken kullanabilirsiniz:

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

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

3

Sorgunuzda bir alt seçim kullanabilirsiniz (denenmemiş ve hangisinin daha hızlı olduğundan emin değilim):

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

Veya

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

Veya

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

Ayrıca saklı bir yordam da kullanabilirsiniz (Firebird sözdizimi için özür dileriz):

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

Buna benzer bir sorunum vardı. doğru sonucu 100 yerine 1,0 ile çarparak elde edebilmelisiniz.

Notu Seç, (Sayım (Sınıf) * 1.0 / (MyTable'dan Sayı (*) Seç)) Sınıfına Göre MyTable Grubundan Puan Olarak Ekteki referans resme bakın


Kesinlikle gerekli olmadıkça lütfen bilgileri resim olarak paylaşmayın. Bkz . Meta.stackoverflow.com/questions/303812/… .
AMC

0

Bu MS SQL iyi çalışıyor. Varchar'ı iki ondalık basamaklı sınırlı şamandıra sonucuna dönüştürür.

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;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.