Cosa fa davvero una funzione di aggregazione
La maggior parte delle funzioni SQL che hai visto finora lavora riga per riga: UPPER(name) viene eseguita una volta per riga, ROUND(price, 2) anche. Le funzioni di aggregazione sono diverse: guardano un intero insieme di righe e lo riducono a un solo valore.
Prepara una piccola tabella per fare qualche prova:
Entrano cinque righe, ne esce una. Tutto il modello mentale è qui: le aggregazioni schiacciano le righe in un riepilogo. Senza un GROUP BY, il riepilogo copre tutte le righe del risultato.
COUNT: righe contro valori
COUNT ha tre forme, e la differenza conta:
COUNT(*)conta le righe. NULL compresi. Restituisce sempre un numero.COUNT(column)conta i valori non NULL di quella colonna.COUNT(DISTINCT column)conta i valori unici non NULL.
Cinque righe, tre con un amount, tre clienti distinti. Se ti capita di vedere COUNT(amount) e ti chiedi perché è più piccolo di COUNT(*), ecco il motivo: i NULL non vengono contati.
SUM, AVG, MIN, MAX
Le aggregazioni aritmetiche funzionano come ti aspetti, con una regola silenziosa: saltano tutte i NULL:
AVG vale (10 + 20 + 30) / 3 = 20.0, non 60 / 4 = 15.0. Il denominatore è il numero di valori non NULL. Se non è quello che vuoi, per esempio se preferisci trattare i dati mancanti come zero, dillo in modo esplicito:
MIN e MAX funzionano anche su testo e date: confrontano il testo in ordine lessicografico e le date come stringhe ISO, se usi il formato di data standard.
SUM contro TOTAL
SQLite ha una seconda aggregazione simile alla somma, TOTAL, che risolve due fastidi di SUM:
SUMsu zero righe restituisceNULL.TOTALrestituisce0.0.SUMsu valori tutti NULL restituisceNULL.TOTALrestituisce0.0.TOTALrestituisce sempre un numero in virgola mobile, quindi non va mai in overflow come l'aritmetica intera.
Il compromesso: TOTAL non è standard, e il risultato sempre REAL può sorprenderti se ti aspettavi un intero. Usalo quando "nessuna riga significa zero" è la risposta giusta per la tua app, e resta su SUM quando vuoi il comportamento standard di SQL.
DISTINCT dentro le aggregazioni
DISTINCT può stare dentro qualsiasi aggregazione, non solo COUNT. Rimuove i valori duplicati prima che l'aggregazione venga eseguita:
SUM(amount) somma l'importo di ogni riga. SUM(DISTINCT amount) somma ogni importo unico una sola volta: utile per cose come "totale degli importi unici delle fatture", ma raramente è quello che ti serve. COUNT(DISTINCT customer) è il caso più comune.
FILTER: aggregare un sottoinsieme
Quando vuoi aggregare solo alcune righe, la mossa ovvia è WHERE. Ma WHERE filtra tutto: così non puoi combinare "conta gli ordini pagati" e "conta i rimborsi" nella stessa query. FILTER risolve il problema:
Ogni clausola FILTER (WHERE ...) si applica solo a quella singola aggregazione. Un solo passaggio sulla tabella, più porzioni riassunte. Prima che esistesse FILTER, si scriveva SUM(CASE WHEN status = 'paid' THEN amount END): stessa idea, più cose da scrivere.
GROUP_CONCAT: unire stringhe
GROUP_CONCAT è quella fuori dal coro. Invece di restituire un numero, concatena i valori in un'unica stringa:
Il separatore predefinito è la virgola. Passa un secondo argomento per usarne un altro. L'ordine non è garantito, a meno che tu non scriva la chiamata come GROUP_CONCAT(tag ORDER BY tag): comodo quando l'output finisce in un'interfaccia e vuoi che resti stabile.
Aggregare senza GROUP BY
Tutti gli esempi finora che usavano aggregazioni senza GROUP BY hanno prodotto esattamente una riga. È questa la regola: una SELECT con aggregazioni e senza GROUP BY è un riepilogo di una sola riga dell'intera tabella (dopo il WHERE).
Puoi combinare le aggregazioni liberamente:
Quello che non puoi fare è mescolare colonne non aggregate con le aggregazioni e aspettarti risultati sensati:
-- Consentito da SQLite, ma il valore di `customer` è arbitrario.
SELECT customer, SUM(amount) FROM orders;
Qui SQLite non dà errore (altri database sì), ma sceglierà il nome di un cliente a caso da mostrare accanto al totale. Se vuoi una somma per cliente, ti serve GROUP BY, che è l'argomento della prossima pagina.
Prossimo passo: GROUP BY e HAVING
Le aggregazioni sull'intera tabella rispondono a "quanto in totale". Le aggregazioni per gruppo, per cliente, per mese, per stato, rispondono alle domande più interessanti. GROUP BY serve a dividere le righe in gruppi prima di aggregare, e HAVING a filtrare sul risultato aggregato. È il prossimo argomento.
Domande frequenti
Cosa sono le funzioni di aggregazione in SQLite?
Sono funzioni che prendono molte righe e restituiscono un unico valore di riepilogo. Quelle integrate sono COUNT, SUM, AVG, MIN, MAX, TOTAL e GROUP_CONCAT. Senza un GROUP BY, riducono l'intero risultato a una sola riga.
Che differenza c'è tra SUM e TOTAL in SQLite?
Entrambe sommano numeri, ma SUM restituisce NULL quando tutti gli input sono NULL e usa l'aritmetica intera quando può (che può andare in overflow). TOTAL restituisce sempre un numero in virgola mobile e dà 0.0 quando non ci sono righe. Usa TOTAL quando vuoi un risultato numerico garantito, SUM quando conta il comportamento standard di SQL.
Come conto i valori distinti in SQLite?
Metti DISTINCT dentro la chiamata: COUNT(DISTINCT customer_id). Così conti i valori unici non NULL. Un semplice COUNT(column) conta i valori non NULL compresi i duplicati, e COUNT(*) conta tutte le righe, NULL o no.
Le funzioni di aggregazione di SQLite ignorano i NULL?
Sì: tutte le funzioni di aggregazione tranne COUNT(*) saltano gli input NULL. AVG divide per il numero di valori non NULL, non per il numero totale di righe. COUNT(*) è l'eccezione: conta righe, non valori, quindi i NULL sono inclusi.