Firebird Documentation Index → Guida sull'uso di NULL nel linguaggio SQL di Firebird → Le funzioni di aggregazione |
Le funzioni di aggregazione – COUNT
,
SUM
, AVG
,
MAX
, MIN
e
LIST
– non gestiscono NULL
allo
stesso modo delle funzioni ordinarie e degli operatori. Invece di
riportare NULL
non appena viene trovato un operando a
NULL
, queste considerano solo i campi non
NULL
per calcolare il risultato. Cioè se avete questa
tabella:
Tabella | ||
---|---|---|
ID | Nome | Importo |
1 | John | 37 |
2 | Jack | NULL |
3 | Jim | 5 |
4 | Joe | 12 |
5 | Josh | NULL |
...eseguendo select sum(Importo) from Tabella
riporta 54, che è appunto 37 + 5 + 12. Se fossero stati sommati tutti i
cinque campi, il risultato sarebbe stato NULL
. Per la
funzione AVG
, i campi non-NULL
sono sommati tra loro, e la somma divisa per il numero dei campi
non-NULL
.
C'è una eccezione a questa regola: COUNT(*)
riporta il numero di tutte le righe, perfino di quelle in cui tutti i
campi sono NULL
. Ma
COUNT
(NomeCampo
) si comporta
come le altre funzioni aggregate, nel senso che conta solo le righe in cui
quello specifico campo non è NULL
.
Un'altra cosa che vale la pena di sapere è che sia
COUNT(*)
sia
COUNT(
non riportano
mai FieldName
)NULL
: se non ci sono righe nell'insieme risultato
della SELECT...
, entrambe valgono 0. Inoltre, anche
COUNT(
vale 0 se
tutti i campi NomeCampo
)NomeCampo
nel risultato sono
NULL
. In questi casi tutte le altre funzioni di
aggregazione riportano NULL
. Bisogna avvertire che
anche SUM
riporta NULL
se
utilizzata su un insieme risultato vuoto, che è contrario alla logica
comune: se non ci sono righe la media, il minimo ed il massimo non sono
definiti, ma la somma è zero.
Ora mettiamo il tutto insieme in una tabella riassuntiva:
Tabella 7. I risultati delle funzioni aggregate in diverse situazioni
Funzione | Risultati | ||
---|---|---|---|
Insieme vuoto | Insieme o colonna tutta a null | Altri casi | |
COUNT(*) | 0 | Numero totale delle righe | Numero totale delle righe |
COUNT(Campo) | 0 | 0 | Numero di righe dove Campo non è
NULL |
MAX, MIN | NULL |
NULL |
Il valore minimo o massimo trovato nella colonna |
SUM | NULL |
NULL |
Somma dei valori non NULL della
colonna
|
AVG | NULL |
NULL |
Media dei valori non NULL della
colonna. Questo vale SUM(Campo) /
COUNT(Campo).[a] |
LIST[b] | NULL |
NULL |
Stringa ottenuta concatenando i valori non
NULL della colonna separati da
virgola
|
[a] Se Campo è un campo di tipo intero, AVG è sempre arrotondato verso 0. Esempio: 6 INT con somma -11 danno media -1, non -2. [b] LIST è stata aggiunta in Firebird 2.1 |
Una clausola GROUP BY non cambia la logica
delle funzioni di aggregazione descritta sopra, eccetto che adesso è
applicata a ciascun gruppo individualmente invece che all'insieme di
righe risultante nel suo insieme. Supponiamo d'avere una tabella
Dipendenti con dei campi Dipartimento e Salario che permettono entrambi
NULL
, e di lanciare questa query:
Il risultato potrebbe essere una cosa del genere (Dipartimento a
<null>
può essere in cima o in fondo, dipende
dalla versione di Firebird):
DIP SOMMA ====== ===================== <null> 219465.19 000 266643.00 100 155262.50 110 130442.81 115 13480000.00 120 <null> 121 110000.00 123 390500.00
Notare che chi ha il dipartimento sconosciuto
(NULL
) è raggruppata inscieme, sebbene non si possa
dire che abbiamo davvero lo stesso valore del campo
Dipartimento. L'alternativa sarebbe stata dare a ciascuno di essi un
«gruppo» a sé. Non solo questo avrebbe generato un
grandissimo numero di linee al risultato, ma avrebbe degenerato lo scopo
del raggruppare: quelle linee non sarebbero un
raggruppamento, ma semplicemente «SELECT
Dipartimento, Salario». Pertanto ha senso raggruppare i
dipartimenti a NULL
secondo il loro stato e gli
altri secondo il loro valore.
Comunque, non è questo che interessa maggiormente. Che cosa ci
dice la colonna SUM? Forse che tutti i salari sono
diversi da NULL
, eccetto nel dipartimento 120? No.
Quel che si può dire è che tranne nel dipartimento 120, c'è nel database
almeno un dipendente con salario noto. Ogni dipartimento
può avere salari a NULL
; nel
120 tutti i salari sono
NULL
.
Per saperne di più, aggiungendo una o più colonne
COUNT(). Per sapere ad esempio il numero dei salari
a NULL
in ciascun gruppo si aggiunge una colonna
«COUNT(*)» oppure «COUNT(Salary)».
Una clausola GROUP BY può essere usata per
trovare le frequenze con cui i valori ricorrono in una tabella. In
questo caso si usa lo stesso nome campo più volte nella query. Con una
tabella TT che ha una colonna
A i cui valori sono { 3, 8,
NULL
, 6, 8, -1, NULL
, 3, 1
}. Per avere la lista delle frequenze, si può fare:
SELECT A, COUNT(A) FROM TT GROUP BY A
che dà il seguente risultato:
A COUNT ============ ============ -1 1 1 1 3 2 6 1 8 2 <null> 0
Oh! Qualcosa non va col conteggio dei NULL
,
ma perchè? Rivediamo:
COUNT(
salta
tutti i campi a Nomecampo
)NULL
, pertanto con
COUNT(
il conteggio del
gruppo A
)<null>
può essere solo 0! Riformulando
la query in questo modo:
SELECT A, COUNT(*) FROM TT GROUP BY A
e si otterrà il valore corretto (nel caso 2).
La clausola HAVING può aggiungere restrizioni ad una query con aggregati, così come la WHERE fa nelle query record per record. La clausola HAVING impone condizioni su ogni colonna del risultato o combinazioni di colonne, che siano aggregate o meno.
Per quanto riguarda i NULL
, vanno tenuti ben
presenti i seguenti due fatti (e credo di non meravigliare nessuno
ormai):
Le righe per le quali la condizione di
HAVING risolve a NULL
non
sono incluse nel risultato finale. («Va bene solo il
true
.»)
«HAVING
<col>
IS [NOT]
NULL» è una condizione lecita e spesso utile,
indipendentemente dal fatto che
<col>
sia raggruppata o meno. (Se
<col>
non è aggregata, si può
risparmiare un po' di lavoro al server cambiando
HAVING in WHERE e mettendolo nelle
condizioni prima del «GROUP BY».
Questo vale per tutte le condizioni su colonne non di
aggregazione.)
Aggiungendo ad esempio la seguente clausola alla query d'esempio del paragrafo «GROUP BY»:
...HAVING Dipartimento IS NOT NULL
si impedisce alla prima riga di essere stampata, mentre con quest'altra:
...HAVING SUM(Salario) IS NOT NULL
sopprima la sesta riga (quella del Dipartimento = 120).
Firebird Documentation Index → Guida sull'uso di NULL nel linguaggio SQL di Firebird → Le funzioni di aggregazione |