Raggruppare i dati con GROUP BY

Gli argomenti di questa pagina

  • Raggruppare dati con GROUP BY o DISTINCT
  • Differenza tra GROUP BY e DISTINCT
  • Confronto tra le clausole DISTINCT e GROUP BY
  • Esempio con clausola DISTINCT
  • Esempio con clausola GROUP BY
  • confronto degli ordinamenti usando le clausole DISTINCT e GROUP BY
  • I campi da utilizzare nella clausola GROUP BY

Raggruppare dati con GROUP BY o DISTINCT

Con la clausola GROUP BY si possono raggruppare dei record per valori omogenei degli campi specificati nella clausola GROUP BY.
La sequenza delle istruzioni in una Query con GROUP BY è quella che segue.

SELECT colonna1, colonna2, colonna3
FROM nome_tabella
WHERE <conditions>
GROUP BY elenco_colonne

Con la clausola DISTINCT i record vengono raggruppati per valori omogenei degli campi specificati nella clausola DISTINCT.
La sequenza delle istruzioni in una Query con DISTINCT è la seguente:

SELECT DISTINCT colonna1, colonna2, colonna3
FROM nome_tabella
ORDER BY elenco_ colonne

Differenza tra GROUP BY e DISTINCT

Apparentemente sembra che GROUP BY e DISTINCT siano simili.
DISTINCT elimina i record ridondanti, cioè che hanno tutti i campi specificati nella clausola DISTINCT uguali nelle varie righe. Per ottenere un determinato ordinamento dei dati è necessario fare uso anche della clausola ORDER BY che vedremo più avanti in questo settore.
GROUP BY è utilizzata in combinazione con le funzioni di aggregazione che si vedranno in seguito e raggruppa il recordset in base ai valori dei campi specificati nella clausola GROUP BY.
La clausola GROUP BY provvede anche ad eseguire un determinato ordinamento dei dati dipendente dall'ordine in cui i campi sono esposti nella clausola.
Nella maggior parte dei casi GROUP BY e DISTINCT sono simili, ma la differenza è che DISTINCT non consente di utilizzare le funzioni di aggregazione che vedremo in seguito, GROUP BY, oltre che permettere di usare funzioni di aggregazione, consente di specificare un numero diverso di campi da quello indicato nella clausola SELECT.

Quindi, come si fa a decidere quale delle due clausole usare?
DISTINCT è più semplice da scrivere e leggere se non si sta aggregando i dati.
GROUP BY è necessario se si utilizzano funzioni di aggregazione dei dati, ma in molti casi è sostituibile a DISTINCT.
Se non si usano funzioni di aggregamento dei dati è possibile scegliere una o l'altra clausola

Tuttavia per quanto riguarda il lavoro che esegue l'interprete SQL le cose cambiano perchè, per restituire dei record univoci, DISTINCT in condizioni ottimali non necessita di leggere tutti i record in quanto esclude o accetta le varie righe man mano che procede nell'elaborazione della Query, mentre GROUP BY prima accetta tutte le righe poi scarta le righe duplicate solo alla fine del processo durante l'esecuzione del GROUP BY.

Vediamo ora alcuni esempi che risolviamo con questo script.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
include ('res/db_login.php');
if (!
$link mysql_connect($Host$User$Password)) {
    echo 
mysql_error() . "<br />\n"
} else {
    if (!
mysql_select_db($DBName)) {
        echo 
mysql_error() . "<br />\n"
    } else {
        
mysql_set_charset("utf8");
        if (
$result mysql_query($Query)) {
            
$num_row mysql_num_rows($result);
            
$numfields mysql_num_fields($result);
            
$fields = array();
            for (
$i=0$i $numfields$i++) {
                
$fields[] = mysql_field_name($result$i);
            }
            if (
$num_row 0) {
                echo 
$num_row " record letti<br />\n";
                echo 
"<table class=\"tabella_it\">\n";        
                echo 
"<tr>\n";
                for (
$i=0$i $numfields$i++) {
                    echo 
"<th>" $fields[$i] . "</th>\n";
                }
                echo 
"</tr>\n";
                while (
$row mysql_fetch_array($result)) {
                    echo 
"<tr>\n";
                    for (
$i=0$i $numfields$i++) {
                        echo 
"<td>";
                        echo 
is_null($row[$fields[$i]]) ? 'NULL' $row[$fields[$i]];
                        echo 
"</td>\n";
                    }
                    echo 
"</tr>\n";
                }
                echo 
"</table>\n";
            } else {
                echo 
"Nessun record restituito<br />\n";
            }
        } else {
            echo 
mysql_error() . "<br />\n"
        }
    }
}
mysql_close ($link);

Confronto tra le clausole DISTINCT e GROUP BY

In questi primi tre esempi possiamo notare che tre differenti sintassi possono ottenere lo stesso risultato.
La richiesta è:

Esempio con clausola DISTINCT

Mostra l'elenco delle città, province e relativi cap (con DISTINCT)

Per questo test uso la clausola DISTINCT. L'ordinamento viene eseguito tramite la clausola ORDER BY usando il solo campo 'cap'.

esempio 1
1
2
3
4
5
6
<?php
$Query 
"SELECT DISTINCT citta, cap, prov
FROM `sql_rubrica`
ORDER BY prov
"
;
?>
22 record trovati
15 record visualizzati
citta cap prov
Abbadia Lariana 67030 AB
Marsiliana 53021 AB
Varena 35031 AE
AbanoTerme 21049 AT
MASSARO 12345 BC
Borgo S. Dalmazzo 57020 BO
Cereseto 73020 BO
Coccanile 71021 BO
Giusvalla 26020 CA
Castel Vittorio 89844 CH
Agropoli 46011 DE
Giusvalla 56020 DE
Condofuri 95022 DO
Corinzio 84042 DO
Correzzana 22050 FS
... ... ...

Esempio con clausola GROUP BY

Eseguo ora lo stesso test usando la clausola GROUP BY indicando nella clausola i tre campi su cui basare il raggruppamento.

Con la clausola GROUP BY l'ordinamento viene eseguito in base all'ordine dei campi indicati nella clausola GROUP BY.

Per quanto riguarda l'ordinamento nel primo esempio la sequenza dei campi usati nella clausola GROUP BY è: citta, cap, prov.
Nel secondo esempio la sequenza è: prov, citta, cap.
Si nota come nei due recordset, mentre il numero dei record restituiti è identico, l'ordine dei record cambia a seconda che si usi la prima o la seconda sequenza.

Mostra l'elenco delle città, province e relativi cap (con GROUP BY)

esempio 2
1
2
3
4
5
6
<?php
$Query 
"SELECT citta, cap, prov
FROM `sql_rubrica`
GROUP BY citta, cap, prov
"
;
?>
esempio 2 a
1
2
3
4
5
6
<?php
$Query 
"SELECT citta, cap, prov
FROM `sql_rubrica`
GROUP BY prov, citta, cap 
"
;
?>
22 record trovati
15 record visualizzati
citta cap prov
Varena 35031 AE
Abbadia Lariana 67030 AB
Ville di Fano 51021 RF
Correzzana 22050 FS
Marsiliana 53021 AB
Falconara Alta 22059 FS
Borgo S. Dalmazzo 57020 BO
San Leonardo in Passiria 51023 RF
Cereseto 73020 BO
Coccanile 71021 BO
AbanoTerme 21049 AT
Bevagna 94014 TA
Castel Vittorio 89844 CH
Condofuri 95022 DO
Condofuri 85022 RO
... ... ...
22 record trovati
15 record visualizzati
citta cap prov
Varena 35031 AE
Abbadia Lariana 67030 AB
Ville di Fano 51021 RF
Correzzana 22050 FS
Marsiliana 53021 AB
Falconara Alta 22059 FS
Borgo S. Dalmazzo 57020 BO
San Leonardo in Passiria 51023 RF
Cereseto 73020 BO
Coccanile 71021 BO
AbanoTerme 21049 AT
Bevagna 94014 TA
Castel Vittorio 89844 CH
Condofuri 95022 DO
Condofuri 85022 RO
... ... ...

confronto degli ordinamenti usando le clausole DISTINCT e GROUP BY

In questa coppia di test uso sia la clausola DISTINCT con la clausola ORDER BY, che la clausola GROUP BY. In entrambi i casi l'ordinamento dei record viene eseguito in base all'ordine dei campi indicati nelle rispettive clausole.
Come è facile notare in entrambi i casi l'interprete SQL restituisce un recordset ordinato alla stessa maniera.

Mostra l'elenco delle città, province e relativi cap (confronto visivo fra i due metodi)

esempio 3
1
2
3
4
5
6
<?php
$Query 
"SELECT DISTINCT citta, cap, prov
FROM `sql_rubrica`
ORDER BY prov, cap, citta
"
;
?>
esempio 3 a
1
2
3
4
5
6
<?php
$Query 
"SELECT citta, cap, prov
FROM `sql_rubrica`
GROUP BY prov, cap, citta
"
;
?>
22 record trovati
15 record visualizzati
citta cap prov
Marsiliana 53021 AB
Abbadia Lariana 67030 AB
Varena 35031 AE
AbanoTerme 21049 AT
MASSARO 12345 BC
Borgo S. Dalmazzo 57020 BO
Coccanile 71021 BO
Cereseto 73020 BO
Giusvalla 26020 CA
Castel Vittorio 89844 CH
Agropoli 46011 DE
Giusvalla 56020 DE
Corinzio 84042 DO
Condofuri 95022 DO
Correzzana 22050 FS
... ... ...
22 record trovati
15 record visualizzati
citta cap prov
Varena 35031 AE
Abbadia Lariana 67030 AB
Ville di Fano 51021 RF
Correzzana 22050 FS
Marsiliana 53021 AB
Falconara Alta 22059 FS
Borgo S. Dalmazzo 57020 BO
San Leonardo in Passiria 51023 RF
Cereseto 73020 BO
Coccanile 71021 BO
AbanoTerme 21049 AT
Bevagna 94014 TA
Castel Vittorio 89844 CH
Condofuri 95022 DO
Condofuri 85022 RO
... ... ...

I campi da utilizzare nella clausola GROUP BY

Come in tutte le operazioni di raggrupamento o di aggregazione il numero di record restituito da un recordset è differente a seconda dei campi utilizzati nella clausola GROUP BY.

Seguono alcuni esempi che mostrano diversi comportamenti a seconda dei campi usati nella clausola GROUP BY.
La Query mostrata nell'esempio 4 è la più restrittiva in quanto mostra un numero più limitato di record.
Le Query mostrate negli esempi 5, 6, 7 restituiscono recordset simili tra loro sia come numero di record che come valori ed associazioni di valori.
Ci si aspetterebbe che le Query di questi esempi restituiscano recordset differenti perchè differenti sono i campi indicati nella clausola GROUP BY. Ma questo è dovuto solo alla natura di questa particolare tabella perchè ad ogni città sono associati cap e prov.
Non può essere che alla città Condofuri con cap 85022 sia associata una provincia differente da RO.
In questo caso, GROUP BY citta, cap - GROUP BY citta, prov - GROUP BY citta, prov, cap degli esempi 5, 6, 7 sono equivalenti.
Non altrettanto si può dire per GROUP BY citta dell'esempio 4 perchè vengono raggruppate le città a prescindere dal cap o prov loro associati.
In questo ultimo caso sarebbe più corretto limitarsi ad indicare il solo campo citta nell'elenco che segue la clausola SELECT in quanto gli altri campi loro associati potrebbero generare confusione nella lettura della tabella.

In questo esempio il raggruppamento avviene sul solo campo citta.

esempio 4
1
2
3
4
5
6
<?php
$Query 
"SELECT cap, citta, prov
FROM `sql_rubrica`
GROUP BY citta
"
;
?>
18 record trovati
15 record visualizzati
cap citta prov
35031 Varena AE
67030 Abbadia Lariana AB
51021 Ville di Fano RF
22050 Correzzana FS
53021 Marsiliana AB
22059 Falconara Alta FS
57020 Borgo S. Dalmazzo BO
51023 San Leonardo in Passiria RF
73020 Cereseto BO
71021 Coccanile BO
21049 AbanoTerme AT
94014 Bevagna TA
89844 Castel Vittorio CH
95022 Condofuri DO
84042 Corinzio DO
... ... ...

Vediamo ora lo stesso esempio aggiungendo un altro campo nella clausola GROUP BY

esempio 5
1
2
3
4
5
6
<?php
$Query 
"SELECT cap, citta, prov
FROM `sql_rubrica`
GROUP BY citta, cap
"
;
?>
22 record trovati
15 record visualizzati
cap citta prov
35031 Varena AE
67030 Abbadia Lariana AB
51021 Ville di Fano RF
22050 Correzzana FS
53021 Marsiliana AB
22059 Falconara Alta FS
57020 Borgo S. Dalmazzo BO
51023 San Leonardo in Passiria RF
73020 Cereseto BO
71021 Coccanile BO
21049 AbanoTerme AT
94014 Bevagna TA
89844 Castel Vittorio CH
95022 Condofuri DO
85022 Condofuri RO
... ... ...

In questo esempio mettiamo il campo 'prov' al posto di 'cap'

esempio 6
1
2
3
4
5
6
<?php
$Query 
"SELECT cap, citta, prov
FROM `sql_rubrica`
GROUP BY citta, prov 
"
;
?>
22 record trovati
15 record visualizzati
cap citta prov
35031 Varena AE
67030 Abbadia Lariana AB
51021 Ville di Fano RF
22050 Correzzana FS
53021 Marsiliana AB
22059 Falconara Alta FS
57020 Borgo S. Dalmazzo BO
51023 San Leonardo in Passiria RF
73020 Cereseto BO
71021 Coccanile BO
21049 AbanoTerme AT
94014 Bevagna TA
89844 Castel Vittorio CH
95022 Condofuri DO
85022 Condofuri RO
... ... ...

Ora usiamo l'intera tripletta di campi.

esempio 7
1
2
3
4
5
6
<?php
$Query 
"SELECT cap, citta, prov
FROM `sql_rubrica`
GROUP BY citta, prov, cap
"
;
?>
22 record trovati
15 record visualizzati
cap citta prov
35031 Varena AE
67030 Abbadia Lariana AB
51021 Ville di Fano RF
22050 Correzzana FS
53021 Marsiliana AB
22059 Falconara Alta FS
57020 Borgo S. Dalmazzo BO
51023 San Leonardo in Passiria RF
73020 Cereseto BO
71021 Coccanile BO
21049 AbanoTerme AT
94014 Bevagna TA
89844 Castel Vittorio CH
95022 Condofuri DO
85022 Condofuri RO
... ... ...

 

 



settore tecnico il sito di lorettabweb il Forum di sostegno
il forum il forum il forum