2009-03-20 14 views
47

ho questa query MySQL:Calcolare un totale parziale in MySQL

SELECT DAYOFYEAR(`date`) AS d, COUNT(*) 
FROM `orders` 
WHERE `hasPaid` > 0 
GROUP BY d 
ORDER BY d 

che restituisce qualcosa di simile:

d | COUNT(*) | 
20 | 5  | 
21 | 7  | 
22 | 12  | 
23 | 4  | 

Quello che mi piace molto è un'altra colonna sulla fine di mostrare la totale corrente:

d | COUNT(*) | ??? | 
20 | 5  | 5 | 
21 | 7  | 12 | 
22 | 12  | 24 | 
23 | 4  | 28 | 

è possibile?

+0

possibile duplicato di [Crea una colonna somma cumulativa in MySQL] (http://stackoverflow.com/questions/2563918/create-a-cumulative-sum-column-in-mysql) – Ztyx

+1

@Ztyx La tua domanda collegata è stata posta più di un anno dopo. Quindi, sarebbe piuttosto il contrario. –

risposta

87

Forse una soluzione più semplice per te e impedisce al database di fare un sacco di domande. Questo esegue solo una query quindi esegue un po 'di matematica sui risultati in un singolo passaggio.

SET @runtot:=0; 
SELECT 
    q1.d, 
    q1.c, 
    (@runtot := @runtot + q1.c) AS rt 
FROM 
    (SELECT 
     DAYOFYEAR(`date`) AS d, 
     COUNT(*) AS c 
    FROM `orders` 
    WHERE `hasPaid` > 0 
    GROUP BY d 
    ORDER BY d) AS q1 

Ciò fornirà una colonna aggiuntiva RT (totale parziale). Non perdere l'istruzione SET in alto per inizializzare la variabile totale parziale o otterrai una colonna di valori NULL.

+1

che funziona brillantemente! Guardando il 'EXPLAIN' su questo mostra che è molto più efficiente rispetto alla risposta precedentemente accettata – nickf

+0

Il punto chiave è utilizzare una sottoquery. Ciò lo rende affidabile in query complesse che coinvolgono più tabelle o aggregazioni. –

+0

Per coloro che desiderano fare qualcosa di simile con le funzioni di base MySQL di PHP, assicurarsi di eseguire la prima riga separatamente (ma ancora prima del 2 °). –

1

Direi che è impossibile che ogni riga risultante sia indipendente. Utilizzare un linguaggio di programmazione per ottenere questi valori

+0

Data la natura della matematica relazionale, e il fatto che tu stia usando group by, anche se mysql ha qualche trucco per renderlo possibile, sarebbe meno complicato farlo semplicemente in un linguaggio di programmazione come suggerisce Sergej. –

+6

Non sarei d'accordo.La suddivisione delle attività di elaborazione tra il database e il livello dell'applicazione è problematica dal punto di vista del riutilizzo e della manutenzione. Se si desidera utilizzare questi dati in posizioni diverse, ad esempio su un report e su uno schermo, è necessario duplicare la logica dei totali correnti. – cdonner

+0

+1 hai ragione: questo sarebbe più semplice e migliore in generale nella logica di programmazione - stavo cercando di vedere se c'era qualche funzione magica impressionante per farlo. – nickf

9
SELECT 
    DAYOFYEAR(O.`date`) AS d, 
    COUNT(*), 
    (select count(*) from `orders` 
     where DAYOFYEAR(`date`) <= d and `hasPaid` > 0) 
FROM 
    `orders` as O 
WHERE 
    O.`hasPaid` > 0 
GROUP BY d 
ORDER BY d 

Ciò richiederà un po 'di messa a punto sintattica (non ho MySQL per provarlo), ma si mostra l'idea. La subquery deve solo tornare indietro e aggiungere tutto ciò che è già incluso nella query esterna, e deve farlo per ogni riga.

Dai un'occhiata a this question per come utilizzare i join per ottenere lo stesso risultato.

Per risolvere i problemi relativi al degrado delle prestazioni con i dati in crescita: poiché sono disponibili max. 366 giorni in un anno e presumo che non si stia eseguendo questa query per più anni, la sottoquery verrà valutata fino a 366 volte. Con gli indici appropriati alla data e al flag hasPaid, sarai a posto.

+0

grazie - funziona perfettamente come è. – nickf

+1

Tieni presente che questo sarà estremamente lento su grandi, medi e alcuni dei piccoli database, perché ha bisogno di fare tante altre query quante saranno le righe nei risultati –

+0

Accetto. Ho fatto +1 su questa risposta perché è intelligente, e tutti abbiamo usato soluzioni come questa quando necessario, ma sappiamo anche che c'è un costo. Dipende da dove hai bisogno del conteggio in esecuzione. Per la logica aziendale? Allora forse lo fai nel DB. Per la vista? Fallo nel codice. –

1

A meno che non si disponga di altre opzioni, ma eseguendo questa operazione in sql, sommerò i risultati nel linguaggio di programmazione che sta creando la query. Un nido come questo diventerà molto lento man mano che la tabella cresce.

+0

Le prestazioni aumenteranno con le dimensioni della tabella, ma non in modo aggressivo poiché il valore viene calcolato e mantenuto. Altri approcci basati su una sottoselezione saranno più costosi. – Brendan

0

È possibile effettuare l'hacking utilizzando l'istruzione Cross Join o alcuni join slef, ma si otterrà lentamente con qualsiasi set di dati di grandi dimensioni, quindi probabilmente è meglio farlo in un processore di query post; o cursore nel codice client

0

Questo è uno dei pochi luoghi in cui i cursori sono più veloci di un set in base query, se le prestazioni sono critiche avrei neanche

  • Fate questo al di fuori di MySql o
  • Utilizzare MySql 5 Cursors
Problemi correlati