So berechnen Sie den Prozentsatz mit einer SQL-Anweisung


177

Ich habe eine SQL Server-Tabelle, die Benutzer und ihre Noten enthält. Nehmen wir der Einfachheit halber an, es gibt 2 Spalten - name& grade. Eine typische Zeile wäre also Name: "John Doe", Note: "A".

Ich suche nach einer SQL-Anweisung, die den Prozentsatz aller möglichen Antworten ermittelt. (A, B, C usw.) Gibt es auch eine Möglichkeit, dies zu tun, ohne alle möglichen Antworten zu definieren (offenes Textfeld - Benutzer können "Bestanden / Nicht Bestanden", "Keine" usw. eingeben).

Die endgültige Ausgabe, die ich suche, ist A: 5%, B: 15%, C: 40% usw.

Antworten:


226

Ich habe folgendes getestet und das funktioniert. Die Antwort von gordyii war knapp, hatte aber die Multiplikation von 100 an der falschen Stelle und einige fehlende Klammern.

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

21
Dies ergibt ein Ergebnis in ganzen Zahlen. Die Summe der Ergebnisse ist nicht gleich 100.
Donner

10
Nicht die effizienteste, da die Tabelle zweimal gescannt wird. Auch die Abfrage sieht nicht so einfach aus, wenn auf mehr als eine Tabelle verwiesen wird.
Alex Aza

14
@Thunder können Sie 100 für 100,0 für Dezimalwerte ändern.
Joseph

Kann jemand erklären, warum die mathematische Syntax der SQL-Abfrage nicht das ist, was Sie normalerweise erwarten würden? Zum Beispiel normal hätte ich durch total dann mal durch 100 geteilt? Wirklich logisch neugierig.
Digitalsa1nt

4
@ Digitalsa1nt (100 * 2) / 4 = 50, (2/4) * 100 = 50, solange der Enumerator der zu multiplizierende Teil ist. Aufgrund der Priorität von SQL-Anweisungen ist dies auch so. Aufgrund von Datentypen bei Verwendung von 100 können Sie das Ergebnis jedoch immer noch auf 0 Dezimalstellen runden, die Sie für% wünschen. Wenn Sie es nach der Teilungsoperation einfügen, müssten Sie sicherstellen, dass Sie einen Datentyp verwenden, der damit umgehen kann die Dezimalstellen sonst erhalten Sie 100 oder 0 und nie einen tatsächlichen Prozentsatz
Matt

231
  1. Am effizientesten (mit over ()).

    select Grade, count(*) * 100.0 / sum(count(*)) over()
    from MyTable
    group by Grade
    
  2. Universal (jede SQL-Version).

    select Grade, count(*) * 100.0 / (select count(*) from MyTable)
    from MyTable
    group by Grade;
    
  3. Mit CTE am wenigsten effizient.

    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 () funktionierte perfekt auf meinem SQL Server 2008, ich habe die Mathematik durchgeführt, um dies zu bestätigen. Um es auf 2 Dezimalstellen abzurunden, habe ich CAST (count ( ) * 100.0 / sum (count ( )) over () AS DECIMAL (18, 2)) verwendet. Danke für den Beitrag!
RJB

3
Falls Sie bei der 100-Multiplikation überlaufen (z. B. arithmetischer Überlauffehler beim Konvertieren des Ausdrucks in den Datentyp int ), ersetzen Sie ihn stattdessen durch eine Division im Nenner:cast((count(*) / (sum(count(*)) over() / 100)) AS DECIMAL(18, 2)) as Percentage
Nikita G.

@RJB Warum müssen Sie mit 100,0 und nicht nur mit 100 multiplizieren, wenn Sie die Ausgabe als Dezimalzahl umwandeln?
AS91

2
@ AS91, da die Umwandlung in eine Dezimalzahl NACH der Divisionsoperation erfolgt. Wenn Sie ein int (100) verlassen, führt das Teilen durch ein anderes int ebenfalls zu einem int, das das Ergebnis rundet. Deshalb besteht der Trick immer darin, der Dividende vor der eigentlichen Division einen Cast
aufzuzwingen

Option 1 mit over()funktioniert hervorragend auf Postgresql 10
James Daily

40

Anstatt einen separaten CTE zu verwenden, um die Gesamtsumme zu erhalten, können Sie eine Fensterfunktion ohne die Klausel "partition by" verwenden.

Wenn Sie verwenden:

count(*)

Um die Anzahl für eine Gruppe zu ermitteln, können Sie Folgendes verwenden:

sum(count(*)) over ()

um die Gesamtzahl zu erhalten.

Beispielsweise:

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

Meiner Erfahrung nach ist es tendenziell schneller, aber ich denke, dass in einigen Fällen intern eine temporäre Tabelle verwendet wird (ich habe "Worktable" gesehen, wenn ich mit "set statistics io on" ausgeführt habe).

BEARBEITEN: Ich bin nicht sicher, ob meine Beispielabfrage das ist, wonach Sie suchen. Ich habe nur veranschaulicht, wie die Fensterfunktionen funktionieren.


+1. Das ist toll. Es kann auch verwendet werden, wenn anstelle von 'table' eine select-Anweisung vorhanden ist.
mr_georg

1
Es wird eine Spule verwendet, in tempdbder sich die Arbeitstabelle befindet. Die logischen Lesevorgänge scheinen höher zu sein, aber sie werden anders als normal gezählt
Martin Smith

1
Tatsächlich würde das COUNT(*) OVER ()in Ihrer Abfrage eine völlig unabhängige Zahl zurückgeben (insbesondere die Anzahl der Zeilen der gruppierten Ergebnismenge). Sie sollten SUM(COUNT(*)) OVER ()stattdessen verwenden.
Andriy M

10

Sie müssen die Gesamtzahl der Noten berechnen. Wenn es sich um SQL 2005 handelt, können Sie CTE verwenden

    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
Dies gibt natürlich nur die Prozentsätze für die in der Tabelle vorhandenen Notencodes an, nicht für diejenigen, die vorhanden sein könnten und nicht. Aber ohne eine endgültige Liste der relevanten (gültigen) Notencodes können Sie es nicht besser machen. Daher die +1 von mir.
Jonathan Leffler

1
Das versteckte Juwel für mich war, dass Sie CONVERT auskommentiert haben.
Chris Catignani

9

Sie müssen sich im Notenfeld gruppieren. Diese Abfrage sollte Ihnen das geben, wonach Sie in so ziemlich jeder Datenbank suchen.

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

Sie sollten das von Ihnen verwendete System angeben.


2
Benötigen Sie nicht auch eine group by-Klausel, da Sie ein Aggregat ('sum (CountofGrade)') in der äußeren Auswahl haben? Und in Standard-SQL könnten Sie '/ (SELECT COUNT (*) FROM Grades)' verwenden, um die Gesamtsumme zu erhalten.
Jonathan Leffler

IBM Informix Dynamic Server mag die nackte SUMME in der Auswahlliste nicht (obwohl sie bei Beschwerden eine etwas weniger hilfreiche Meldung ausgibt). Wie in meiner Antwort und meinem vorherigen Kommentar erwähnt, funktioniert die Verwendung eines vollständigen Unterauswahlausdrucks in der Auswahlliste in IDS.
Jonathan Leffler

Dies ist auch besser, weil man komplexe wo auf innere Abfragen anwenden kann.
MVMN

9

Ich benutze dies einfach immer dann, wenn ich einen Prozentsatz berechnen muss.

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

Beachten Sie, dass 100.0 Dezimalstellen zurückgibt, während 100 allein das Ergebnis auf die nächste ganze Zahl aufrundet, selbst mit der Funktion ROUND ()!


7

Folgendes sollte funktionieren

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

BEARBEITEN: Verschob das * 100und fügte das hinzu, 1.0um sicherzustellen, dass keine Ganzzahldivision durchgeführt wird

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

1
Dies funktioniert, aber die Antworten werden alle als 0 zurückgegeben. Muss ich eine Art Zahlenformatierung oder -konvertierung durchführen, um die richtige Antwort zu erhalten?
Alex

1
Wählen Sie Note, rund (Anzahl (Note) * 100,0 / ((Anzahl (Note) aus Noten auswählen) * 1,0), 2) Aus Noten Gruppieren nach Note, um eine runde Funktion in SQL Server-Rückkehr hinzuzufügen, z. B.: 21.56000000000
Thunder

5

Dies ist meines Erachtens eine allgemeine Lösung, obwohl ich sie mit IBM Informix Dynamic Server 11.50.FC3 getestet habe. Die folgende Abfrage:

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;

gibt die folgende Ausgabe für die Testdaten aus, die unter der horizontalen Regel angezeigt werden. Die ROUNDFunktion kann DBMS-spezifisch sein, der Rest (wahrscheinlich) nicht. (Beachten Sie, dass ich 100 in 100.0 geändert habe, um sicherzustellen, dass die Berechnung mit einer nicht ganzzahligen - DECIMAL, NUMERIC - Arithmetik erfolgt. Siehe die Kommentare und danke an 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');

gibt ganzzahlige Prozent in SQL-Server
Thunder

@Thunder: interessant; Was passiert, wenn Sie beispielsweise die 100 auf 100,00 ändern?
Jonathan Leffler

Sicher ist das Ergebnis in Dezimalzahl mit 100.0
Thunder

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

3

In jeder SQL Server-Version können Sie eine Variable für die Summe aller Noten wie folgt verwenden:

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

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

3

Sie können eine Unterauswahl in Ihrer from-Abfrage verwenden (ungetestet und nicht sicher, welche schneller ist):

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

Oder

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

Oder

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

Sie können auch eine gespeicherte Prozedur verwenden (Entschuldigung für die Firebird-Syntax):

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

Ich hatte ein ähnliches Problem. Sie sollten in der Lage sein, das richtige Ergebnis mit 1,0 anstelle von 100 zu multiplizieren. Siehe Beispielbild im Anhang

Wählen Sie Note, (Anzahl (Note) * 1,0 / (Anzahl (*) aus MyTable auswählen)) als Punktzahl aus MyTable-Gruppe nach Note Siehe Referenzbild im Anhang


Bitte geben Sie keine Informationen als Bilder weiter, es sei denn, dies ist unbedingt erforderlich. Siehe: meta.stackoverflow.com/questions/303812/… .
AMC

0

Dieser funktioniert gut in MS SQL. Es transformiert varchar in das Ergebnis eines Floats mit zwei Dezimalstellen.

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;
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.