Ricordiamo che le funzioni di aggregazione sono quelle che lavorano subito con più righe di dati e restituiscono un solo risultato. In PostgreSQL userai spesso queste funzioni di aggregazione:
SUM()— somma dei dati.AVG()— calcolo del valore medio.MIN()— ricerca del valore minimo.MAX()— ricerca del valore massimo.COUNT()— conteggio delle righe.
A prima vista sembra tutto facile: passi una colonna o un'espressione alla funzione e ottieni il risultato. Ma cosa succede se nella colonna c'è un NULL?
Comportamento di NULL negli aggregati: panoramica veloce
Qui inizia la parte interessante:
SUM()eAVG()ignoranoNULL. Se almeno un record ha valoreNULL, semplicemente non viene considerato nei calcoli. Un comportamento abbastanza logico: come può crescere la somma se qualcuno "non è venuto alla festa"? O come calcoli la media se manca un valore?MIN()eMAX()saltano anche loro iNULL. Cercano il valore minimo o massimo solo tra i dati che non sonoNULL. Quindi, se cerchi lo studente più giovane che ha dimenticato di inserire la data di nascita,NULLnon vincerà mai.COUNT(*)conta tutte le righe, anche quelle dove c'èNULL. InveceCOUNT(column)conta solo le righe dove nella colonna specificata c'è un valore, cioèNULLviene ignorato.
Dai, vediamolo con degli esempi.
Esempi di uso delle funzioni di aggregazione con NULL
Ecco la tabella students_scores, che contiene i voti degli studenti per un test:
| student_id | name | score |
|---|---|---|
| 1 | Alisa | 85 |
| 2 | Bob | NULL |
| 3 | Charli | 92 |
| 4 | Dana | NULL |
| 5 | Elena | 74 |
Ora facciamo qualche query e analizziamo i risultati:
- Somma di tutti i voti:
SUM()
SELECT SUM(score) AS total_score
FROM students_scores;
Risultato:
| total_score |
|---|
| 251 |
Come vedi, i valori NULL mancanti non sono stati sommati. Per Alisa (85), Charli (92) ed Elena (74) la somma è 251. Bob e Dana sono rimasti fuori.
- Voto medio:
AVG()
SELECT AVG(score) AS average_score
FROM students_scores;
Risultato:
| average_score |
|---|
| 83.67 |
Di nuovo, NULL sono stati ignorati e la media è stata calcolata solo per chi ha un voto: (85 + 92 + 74) / 3 = 83.67.
- Voto minimo e massimo:
MIN()eMAX()
SELECT
MIN(score) AS min_score,
MAX(score) AS max_score
FROM students_scores;
Risultato:
| min_score | max_score |
|---|---|
| 74 | 92 |
Anche qui tutto semplice: i valori NULL sono stati ignorati, il minimo è 74 e il massimo è 92.
- Conteggio delle righe:
COUNT(*)vsCOUNT(column)
SELECT
COUNT(*) AS total_rows,
COUNT(score) AS non_null_scores
FROM students_scores;
Risultato:
| total_rows | non_null_scores |
|---|---|
| 5 | 3 |
COUNT(*)ha contato tutte le righe, anche quelle dovescoreèNULL.COUNT(score)ha contato solo le righe dove nella colonnascorec'è un valore.
Casi pratici
Vediamo qualche esempio pratico.
Esempio 1: Conteggio dei dipendenti con e senza stipendio indicato
Supponiamo di avere una tabella employees con gli stipendi.
| id | name | salary |
|---|---|---|
| 1 | Alex Lin | 50000 |
| 2 | Maria Chi | NULL |
| 3 | Anna Song | 60000 |
| 4 | Otto Art | NULL |
| 5 | Liam Park | 55000 |
Vogliamo sapere quanti dipendenti hanno indicato lo stipendio e quanti no.
SELECT
COUNT(*) AS total_employees,
COUNT(salary) AS employees_with_salary,
COUNT(*) - COUNT(salary) AS employees_without_salary
FROM employees;
Qui:
COUNT(*)restituisce il numero totale di dipendenti.COUNT(salary)conta quanti hanno indicato lo stipendio.- Per calcolare quanti non hanno lo stipendio, basta sottrarre un valore dall'altro.
Risultato
| total_employees | employees_with_salary | employees_without_salary |
|---|---|---|
| 5 | 3 | 2 |
Esempio 2: Calcolo del prezzo medio dei prodotti considerando i dati mancanti
Sei il proprietario di un negozio magico e nella tabella products c'è la colonna price, ma per alcuni prodotti il prezzo non è ancora indicato.
| id | name | price |
|---|---|---|
| 1 | Magic Wand | 150 |
| 2 | Enchanted Cloak | NULL |
| 3 | Potion Bottle | 75 |
| 4 | Spell Book | 200 |
| 5 | Crystal Ball | NULL |
Devi sapere il prezzo medio solo per i prodotti dove è stato inserito.
SELECT AVG(price) AS average_price
FROM products;
Risultato:
| average_price |
|---|
| 141.6667 |
Se vuoi impostare un prezzo di default per i prodotti senza prezzo (ad esempio 0), puoi usare la funzione COALESCE() che vedremo nella prossima lezione.
Esempio 3: Trovare l'età minima e massima degli studenti
Nella tabella students c'è l'età degli studenti, ma per alcuni non è nota (NULL).
| id | name | age |
|---|---|---|
| 1 | Alex Lin | 20 |
| 2 | Maria Chi | NULL |
| 3 | Anna Song | 19 |
| 4 | Otto Art | 22 |
| 5 | Liam Park | NULL |
Vogliamo sapere chi è lo studente più giovane e quello più anziano.
SELECT
MIN(age) AS youngest_student,
MAX(age) AS eldest_student
FROM students;
Risultato:
| youngest_student | eldest_student |
|---|---|
| 19 | 22 |
Questa query restituisce l'età minima e massima solo per gli studenti che hanno l'età indicata. NULL viene di nuovo saltato.
Particolarità e insidie
Quando lavori con NULL negli aggregati, ricordati questi punti:
- Nella somma
SUM()e nella mediaAVG()iNULLnon vengono considerati. Puoi sfruttarlo per non aggiungere valori "vuoti" nei calcoli. - Se vuoi contare anche le righe con
NULLin una colonna, usaCOUNT(*). - Con
MIN()oMAX(),NULLnon influisce sul risultato. Però, se tutta la colonna è soloNULL, anche il risultato saràNULL.
Consigli per lavorare con NULL
- Considera il contesto del problema. È importante capire se devi considerare i
NULLnella tua query. A volte, come conAVG(), ignorarli è proprio quello che serve. Altre volte, come quando conti il totale, è importante includere anche le righe conNULL. - Usa
COALESCE()se serve. Se vuoi sostituireNULLcon un valore di default nei calcoli, la funzioneCOALESCE()diventerà la tua amica (ma questa è roba per la prossima lezione). - Non confondere
COUNT(*)eCOUNT(column). Questo è un errore classico dei principianti. Il primo conta tutte le righe, il secondo solo quelle con valori non nulli.
Ora sai come il silenzioso NULL può influenzare gli aggregati. Questa conoscenza ti aiuterà ad evitare brutte sorprese e a usare NULL a tuo vantaggio. Nella prossima lezione vedremo il potente strumento COALESCE() per gestire i NULL ancora meglio.
GO TO FULL VERSION