Come calcolare la percentuale con un'istruzione SQL


177

Ho una tabella di SQL Server che contiene gli utenti e i loro voti. Per semplicità, diciamo solo che ci sono 2 colonne - name& grade. Quindi una riga tipica sarebbe Nome: "John Doe", Grado: "A".

Sto cercando un'istruzione SQL che troverà le percentuali di tutte le possibili risposte. (A, B, C, ecc ...) Inoltre, c'è un modo per farlo senza definire tutte le possibili risposte (campo di testo aperto - gli utenti possono inserire "passa / fallisci", "nessuno", ecc ...)

L'output finale che sto cercando è A: 5%, B: 15%, C: 40%, ecc ...

Risposte:


227

Ho testato quanto segue e questo funziona. La risposta di gordyii era vicina ma aveva la moltiplicazione di 100 nel posto sbagliato e aveva alcune parentesi mancanti.

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

21
questo dà risultato in numeri interi. la somma dei risultati non è uguale a 100.
Tuono

10
Non è il più efficiente in quanto il tavolo verrà scansionato due volte. Inoltre, la query non sembrerà così semplice se si fa riferimento a più di una tabella.
Alex Aza,

14
@Thunder è possibile modificare da 100 a 100.0 per i valori decimali.
Joseph,

Qualcuno può spiegare perché la sintassi matematica della query SQL non è quella che ti aspetteresti di fare normalmente? Ad esempio normale avrei diviso per totale quindi i tempi per 100? Sinceramente curioso di questo da un punto di vista logico.
Digitalsa1nt

4
@ Digitalsa1nt (100 * 2) / 4 = 50, (2/4) * 100 = 50 fintanto che l'enumeratore è la parte da moltiplicare. A causa della precedenza delle istruzioni SQL sarà lo stesso. tuttavia, a causa dei tipi di dati se si utilizza 100 è comunque possibile ottenere il risultato arrotondato a 0 decimali desiderati per la% dove, come se lo si inserisse dopo l'operazione di divisione, è necessario assicurarsi di eseguire il cast su un tipo di dati in grado di gestire le cifre decimali altrimenti finirai con 100 o 0 e mai una percentuale effettiva
Matt

232
  1. Il più efficiente (utilizzando over ()).

    select Grade, count(*) * 100.0 / sum(count(*)) over()
    from MyTable
    group by Grade
    
  2. Universale (qualsiasi versione di SQL).

    select Grade, count(*) * 100.0 / (select count(*) from MyTable)
    from MyTable
    group by Grade;
    
  3. Con CTE, il meno efficiente.

    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 () ha funzionato perfettamente sul mio SQL Server 2008, ho fatto i conti in matematica per confermare. Per arrotondare al secondo decimale ho usato CAST (count ( ) * 100.0 / sum (count ( )) over () AS DECIMAL (18, 2)). Grazie per il post!
RJB

3
In caso di overflow sulla moltiplicazione 100 (ad es. Errore di overflow aritmetico durante la conversione dell'espressione in tipo di dati int ), sostituirlo invece con divisione in denominatore:cast((count(*) / (sum(count(*)) over() / 100)) AS DECIMAL(18, 2)) as Percentage
Nikita G.

@RJB Perché devi moltiplicare per 100.0 e non solo per 100 quando stai lanciando l'output come un decimale?
AS91,

2
@ AS91, perché il cast in decimale si verifica DOPO l'operazione di divisione. Se si lascia un int (100), la divisione per un altro int comporterà anche un int, che arrotonderà il risultato. Questo è il motivo per cui il trucco è sempre quello di forzare un cast sul dividendo prima della divisione effettiva (puoi moltiplicare per un decimale letterale come 1.0 o lanciare / convertire)
luiggig

L'opzione 1 con over()funziona alla grande su Postgresql 10
James Daily,

40

Invece di utilizzare un CTE separato per ottenere il totale, è possibile utilizzare una funzione di finestra senza la clausola "partizione per".

Se stai usando:

count(*)

per ottenere il conteggio per un gruppo, è possibile utilizzare:

sum(count(*)) over ()

per ottenere il conteggio totale.

Per esempio:

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

Tende ad essere più veloce nella mia esperienza, ma penso che potrebbe usare internamente una tabella temporanea in alcuni casi (ho visto "Worktable" quando corro con "imposta statistiche io on").

EDIT: Non sono sicuro se la mia query di esempio è ciò che stai cercando, stavo solo illustrando come funzionano le funzioni di windowing.


+1. Questo è fantastico Può anche essere usato se al posto di 'table' c'è un'istruzione select.
mr_georg,

1
Utilizza una bobina in tempdbcui è il tavolo di lavoro. Le letture logiche sembrano più alte ma sono contate in modo diverso dal normale
Martin Smith,

1
In realtà, COUNT(*) OVER ()nella query verrà restituita una figura completamente non correlata (in particolare, il numero di righe del set di risultati raggruppati ). Dovresti usare SUM(COUNT(*)) OVER ()invece.
Andriy M,

10

Devi calcolare il totale dei voti Se è SQL 2005 puoi usare 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
Naturalmente, ciò fornisce solo le percentuali per i codici di valutazione presenti nella tabella, non per quelli che potrebbero essere presenti e non lo sono. Ma senza un elenco definitivo dei codici di valutazione pertinenti (validi), non puoi fare di meglio. Da qui il +1 da parte mia.
Jonathan Leffler,

1
La gemma nascosta per me era che hai commentato CONVERT.
Chris Catignani,

9

Devi raggruppare nel campo del voto. Questa query dovrebbe darti quello che stai cercando praticamente in qualsiasi database.

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

Devi specificare il sistema che stai utilizzando.


2
Dato che hai un aggregato ('sum (CountofGrade)') nella selezione esterna, non hai bisogno di una clausola group by anche in esso? E in SQL standard, penso che potresti usare '/ (SELECT COUNT (*) FROM Grades)' per ottenere il totale generale.
Jonathan Leffler,

IBM Informix Dynamic Server non ama il SUM nudo nell'elenco di selezione (anche se fornisce un messaggio un po 'meno utile quando si lamenta). Come notato nella mia risposta e nel mio precedente commento, l'uso di un'espressione di sub-selezione completa nell'elenco di selezione funziona in IDS.
Jonathan Leffler,

Questo è anche meglio perché si può applicare complesse dove alla query interna.
MVV

9

Lo uso semplicemente ogni volta che ho bisogno di elaborare una percentuale ...

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

Nota che 100.0 restituisce decimali, mentre 100 da solo arrotonda il risultato al numero intero più vicino, anche con la funzione ROUND ()!


7

Quanto segue dovrebbe funzionare

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

EDIT: spostato * 100e aggiunto il 1.0per assicurarsi che non esegua la divisione intera

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

1
funziona, ma le risposte tornano tutte come 0 - devo fare una sorta di formattazione o conversione del numero per vedere la risposta corretta?
Alex,

1
Seleziona grado, rotondo (conteggio (grado) * 100,0 / ((Seleziona conteggio (grado) dai voti) * 1,0), 2) dai voti Raggruppa per grado per aggiungere una funzione rotonda nel ritorno al server sql, ad es .: 21.56000000000
Tuono

5

Questa è, credo, una soluzione generale, sebbene l'ho testata utilizzando IBM Informix Dynamic Server 11.50.FC3. La seguente query:

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;

fornisce il seguente output sui dati di test mostrati sotto la regola orizzontale. La ROUNDfunzione può essere specifica per DBMS, ma il resto (probabilmente) non lo è. (Nota che ho cambiato da 100 a 100.0 per assicurarmi che il calcolo avvenga usando un numero non intero - DECIMAL, NUMERIC - aritmetico; vedi i commenti e grazie a 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');

fornisce la percentuale intera in sql-server
Thunder il

@Thunder: interessante; cosa succede se cambi, diciamo, da 100 a 100,00?
Jonathan Leffler,

Sicuramente il risultato è in decimale con 100.0
Thunder il

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

3

In qualsiasi versione di SQL Server è possibile utilizzare una variabile per il totale di tutti i gradi come questo:

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

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

3

È possibile utilizzare una sottoselezione nella query from (non testata e non sicura di quale sia la più veloce):

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

O

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

O

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

Puoi anche usare una procedura memorizzata (scuse per la sintassi di 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

Ho avuto un problema simile a questo. dovresti essere in grado di ottenere il risultato corretto moltiplicando per 1,0 anziché 100. Vedi esempio Immagine allegata

Seleziona il grado, (Count (Grade) * 1.0 / (Select Count (*) From MyTable)) come punteggio dal gruppo MyTable per grado Vedi l'immagine di riferimento allegata


Si prega di non condividere informazioni come immagini se non assolutamente necessario. Vedi: meta.stackoverflow.com/questions/303812/… .
AMC

0

Questo funziona bene in MS SQL. Trasforma varchar nel risultato di un float a due cifre decimali.

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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.