English

‘GROUP BY’ Explanation

In SQL, the GROUP BY clause allows to perform calculations on the tuples of a table, based on one or more specified attributes’ values.
Basically, starting from the source table, many virtual tables are created according to the distinct values for a given set of attributes.
The aggregate functions (e.g. COUNT, AVG, MAX, etc.) are then used on virtual tables, in order to get the desired results.

The HAVING clause may be used ONLY when paired with GROUP BY, as it allows to filter in respect to the used aggregate operation.

A graphical example is worth a thousand words.
Suppose the existence of the following example table T:

+=======+
| A | B |
+=======+
| 1 | 2 |
| 1 | 3 |
| 2 | 3 |
| 2 | 1 |
| 3 | 0 |
+-------+

Therefore, it is asked to “Get the number of DISTINCT values of A”; In short, the number of occurrences for the ‘unique’ values associated to the A attribute. At a quick glance, it is clear that for the attribute A, there exist two occurrences for 1, two occurrences for 2 and one for 3.

This kind of problem is easily solvable by using GROUP BY.
The SQL query that solves this, is the following:

SELECT A, COUNT(A) AS n FROM T GROUP BY A;

Please note that were selected both, A and its tally, renamed as ‘n’, and the whole thing is grouped by A.
To understand how this is calculated, one might imagine “virtually dividing” T into as many virtual tables as there are unique values of A; In this case, having three unique values (1, 2, and 3), makes three virtual tables.

Based on this, there will be:

  • T_1, which contains all tuples in which A = 1;
  • T_2, which contains all tuples in which A = 2;
  • T_3, which contains all tuples in which A = 3;

Graphically, these are represented as:

  • Table T_1:

    +=======+
    | A | B |
    +=======+
    | 1 | 2 |
    | 1 | 3 |
    +-------+
  • Table T_2:

    +=======+
    | A | B |
    +=======+
    | 2 | 3 |
    | 2 | 1 |
    +-------+
  • Table T_3:

    +=======+
    | A | B |
    +=======+
    | 3 | 0 |
    +-------+

After that, the particular aggregate function is executed on each table’s tuples; In this case, COUNT(A).
Needless to say, for both T_1 and T_2, COUNT(A) is 2, for T_3 COUNT(A) is 1.
Wow, they are the same values detected with a quick glance!

All that remains is to take these values and associate them with the particular A’s ‘unique’ value (in the SELECT clause, first A, then n is queried).
The results are:

+---+---+
| A | n |
+---+---+
| 1 | 2 |
| 1 | 2 |
| 2 | 1 |
+---+---+

It is necessary to gather experience, in order to understand when to use GROUP BY and HAVING.
In general, if there’s necessity to count, to calculate the mean, to detect the minimum/maximum and similar, it’s 90% GROUP BY material.
Of course it’s not always like that, usual mistakes in exam tests include reading “for every element” and jump to the conclusion that a GROUP BY clause must be used; That’s simply wrong; It highly depends on the Database schema one’s dealing with.

Usage of HAVING

Here follows the usage of the clause HAVING.
To the previous request, a further condition to ignore all values of A that have less than two correspondences, is added.
To do this, it is sufficient to alter the query this way:

SELECT A, COUNT(A) AS n FROM T GROUP BY A HAVING n < 2;

As a consequence, graphically, the result is:

+---+---+
| A | n |
+---+---+
| 1 | 2 |
| 2 | 2 |
+---+---+

Usage of multiple attributes with GROUP BY

It is important to note that in the first section of this page, it has been said explicitly that GROUP BY:

“[…] allows to perform calculations on the tuples of a table, based on one or more specified attributes’ values. […]”

It is in fact possible to specifiy different attributes; The aggregation function will produce different results, accordingly.
Considering the query:

SELECT A, COUNT(A) AS n FROM T GROUP BY A, B;

If executed, the result will be:

+---+---+
| A | n |
+---+---+
| 1 | 1 |
| 1 | 1 |
| 2 | 1 |
| 2 | 1 |
| 3 | 1 |
+---+---+

What’s the criteria this time?
Always the same, except it was not told, up to now, that GROUP BY “makes virtual tables” based on a tuple of attributes; This means that specifying a single attribute, is like specifying a tuple containing only a single attribute.
Considering the previous examples, GROUP BY A makes virtual tables in respect to the tuple (A), and GROUP BY A, B, makes them in respect to (A, B).

Looking at the table T, one may notice that, if during the virtual tables definition, a value is considered depending on (A, B), then it will be a pair, in which the left term is a value for A, and the right term is a value for B.
In this scenario, the “distinct tuples” are:

(1, 2)
(1, 3)
(2, 3)
(2, 1)
(3, 0)

That is, EVERY SINGLE ONE.
This explains the result; Fittingly, not being repeated tuples, each one of them counts as 1.
Note that even though COUNT operates only on A, it’s as if it counts the pair A and B, since A is within the tuple (A, B), which is considered a “whole”.

This can be checked by adding a duplicated tuple, for example (1, 2) to T:

+=======+
| A | B |
+=======+
| 1 | 2 |
| 1 | 3 |
| 2 | 3 |
| 2 | 1 |
| 3 | 0 |
| 1 | 2 |
+-------+

By using the previous query, the result is:

+---+---+
| A | n |
+---+---+
| 1 | 2 |
| 1 | 1 |
| 2 | 1 |
| 2 | 1 |
| 3 | 1 |
+---+---+

By selecting B too, the query becomes:

SELECT A, B, COUNT(A) as n from T GROUP BY A, B;

It returns:

+---+---+---+
| A | B | n |
+---+---+---+
| 1 | 2 | 2 |
| 1 | 3 | 1 |
| 2 | 1 | 1 |
| 2 | 3 | 1 |
| 3 | 0 | 1 |
+---+---+---+

SQL code for MySQL/SQLite3

One may test what has been discussed up to now, by using the following SQL instructions.
In MySQL/MariaDB, invoke the command-line client and use the commands:

CREATE DATABASE groupbytestdb;
USE DATABASE groupbytestdb;

With this code, one may make a test database named ‘groupbytestdb’.
For SQLite3, it is sufficient to invoke the program, even without arguments.

Regardless of the particular DBMS, it is now possible to make the table T and insert example tuples:

CREATE TABLE T (
    A INTEGER,
    B INTEGER
);
INSERT INTO T (A, B) VALUES (1, 2);
INSERT INTO T (A, B) VALUES (1, 3);
INSERT INTO T (A, B) VALUES (2, 3);
INSERT INTO T (A, B) VALUES (2, 1);
INSERT INTO T (A, B) VALUES (3, 0);

To test multi-attributes GROUP BY too, add a duplicated tuple:

INSERT INTO T (A, B) VALUES (1, 2);

Italiano

Spiegazione di ‘GROUP BY’

In SQL, La clausola GROUP BY consente di eseguire calcoli sulle tuple di una tabella, in base ai valori di uno o più attributi specificati.
Il funzionamento è più o meno il seguente: Partendo dalla tabella d’origine, vengono create tante tabelle virtuali quanti sono i valori distinti di un dato insieme di attributi.
Su queste tabelle virtuali, vengono poi eseguiti le funzioni aggregate (ad es. COUNT, AVG, MAX, ecc.), per ottenere i risultati desiderati.

La clausola HAVING può essere usata SOLO in accoppiata con GROUP BY, e consente di filtrare rispetto all’operazione d’aggregazione utilizzata.

Un esempio grafico vale più di mille parole.
Si ponga di avere la seguente tabella T d’esempio:

+=======+
| A | B |
+=======+
| 1 | 2 |
| 1 | 3 |
| 2 | 3 |
| 2 | 1 |
| 3 | 0 |
+-------+

Ergo, venga richiesto di “Ottenere il numero di valori DISTINTI di A”; In parole povere, desideriamo ottenere il numero di occorrenze dei valori ‘unici’ dell’attributo A.
Ad uno sguardo veloce, si capisce che per l’attributo A, si hanno due occorrenze del valore 1, due per il valore 2 e una sola per il valore 3.

Questo tipo di problema è facilmente risolvibile sfruttando GROUP BY.
La query SQL che lo risolve è la seguente:

SELECT A, COUNT(A) AS n FROM T GROUP BY A;

Si noti che è stato selezionato A e il suo conteggio rinominato in ‘n’, il tutto, raggruppato rispetto ad A.
Per comprendere come viene calcolato il risultato, occorre immaginare di “suddividere virtualmente” T, in tante tabelle virtuali quanti sono i valori unici di A; In questo caso, sussistono tre valori unici (1, 2 e 3), ergo, tre tabelle virtuali.

In base a ciò, si avrà:

  • T_1, che conterrà tutte le tuple per cui A = 1;
  • T_2, che conterrà tutte le tuple per cui A = 2;
  • T_3, che conterrà tutte le tuple per cui A = 3;

Graficamente, queste vengono rappresentate così:

  • Tabella T_1:

    +=======+
    | A | B |
    +=======+
    | 1 | 2 |
    | 1 | 3 |
    +-------+
  • Tabella T_2:

    +=======+
    | A | B |
    +=======+
    | 2 | 3 |
    | 2 | 1 |
    +-------+
  • Tabella T_3:

    +=======+
    | A | B |
    +=======+
    | 3 | 0 |
    +-------+

Ottenute le tabelle, viene eseguita la particolare funzione aggregata sulle tuple di ciascuna; In questo caso, COUNT(A).
Inutile dire che per T_1 e T_2, COUNT(A) è 2, per T_3 COUNT(A) è 1.
Ma guarda, sono gli stessi valori ottenibili col semplice sguardo!

Ora rimane da prendere questi valori, ed associarli ognuno con il particolare valore di A (nella SELECT viene chiesto prima A, poi n).
Il risultato sarà:

+---+---+
| A | n |
+---+---+
| 1 | 2 |
| 1 | 2 |
| 2 | 1 |
+---+---+

Occorre fare esperienza, per capire quando usare GROUP BY e HAVING.
In generale, se occorre contare, calcolare lla media, individuare il massimo/minimo e simili, al 90% occorre GROUP BY.
Ovviamente non è sempre così, errori comuni nelle prove d’esame includono il leggere “per ogni elemento” e partire in quarta nel credere che occorra utilizzare GROUP BY; Niente di più sbagliato; Dipende molto dallo schema di Database con cui si ha a che fare.

Uso di HAVING

Qui segue un uso della clausola HAVING.
Alla richiesta precedente, viene aggiunta la condizione di ignorare tutti i valori di A che abbiano meno di due corrispondenze.
Per fare ciò, basta alterare la query in questo modo:

SELECT A, COUNT(A) AS n FROM T GROUP BY A HAVING n < 2;

Di conseguenza, si avrà, graficamente:

+---+---+
| A | n |
+---+---+
| 1 | 2 |
| 2 | 2 |
+---+---+

Uso di molteplici attributi in GROUP BY

È importante notare che nella prima sezione di questa pagina, viene detto esplicitamente che GROUP BY:

“[…] consente di eseguire calcoli sulle tuple di una tabella, in base ai valori di uno o più attributi specificati. […]”

È infatti possibile specificare molteplici attributi; La funzione di aggregazione produrrà risultati diversi, come conseguenza.
Si consideri la query:

SELECT A, COUNT(A) AS n FROM T GROUP BY A, B;

Se eseguita, si otterrà il risultato:

+---+---+
| A | n |
+---+---+
| 1 | 1 |
| 1 | 1 |
| 2 | 1 |
| 2 | 1 |
| 3 | 1 |
+---+---+

Qual’è il criterio questa volta?
Sempre lo stesso, solo che non è stato fin’ora, che GROUP BY “crea le tabelle virtuali” in base ad una tupla di attributi; Questo significa che lo specificare un singolo attributo, è come specificare una tupla contenente un singolo attributo.
Considerando gli esempi precedenti, GROUP BY A, crea tabelle virtuali rispetto alla tupla (A), e GROUP BY A, B, le crea in base a (A, B).

Rivedendo la tabella T, si constata che, se durante la suddivisione in tabelle virtuali, un valore è considerato in base a (A, B), allora esso sarà una coppia di il cui termine di sinistra è un valore di A, mentre il termine destro è un valore di B.
In questo scenario, le “tuple distinte” sono:

(1, 2)
(1, 3)
(2, 3)
(2, 1)
(3, 0)

Ovvero, TUTTE.
Questo spiega il risultato; Giustamente, non essendoci tuple ripetute, ognuna di esse conta come singola occorrenza.
Si noti che anche se COUNT opera solo su A, è come se si conteggiasse la coppia e non il singolo attributo, questo perchè È LA COPPIA di attributi (A, B) ad essere considerata un’“unità”.

Questo lo si può constatare aggiungendo alla tabella T, una tupla duplicata, ad esempio (1, 2):

+=======+
| A | B |
+=======+
| 1 | 2 |
| 1 | 3 |
| 2 | 3 |
| 2 | 1 |
| 3 | 0 |
| 1 | 2 |
+-------+

Sfruttando la query precedente, si ottiene:

+---+---+
| A | n |
+---+---+
| 1 | 2 |
| 1 | 1 |
| 2 | 1 |
| 2 | 1 |
| 3 | 1 |
+---+---+

Includendo anche B nella query:

SELECT A, B, COUNT(A) as n from T GROUP BY A, B;

Si otterrà il risultato:

+---+---+---+
| A | B | n |
+---+---+---+
| 1 | 2 | 2 |
| 1 | 3 | 1 |
| 2 | 1 | 1 |
| 2 | 3 | 1 |
| 3 | 0 | 1 |
+---+---+---+

Codice SQL per MySQL/SQLite3

È possibile testare quanto discusso fin’ora, mediante le seguenti istruzioni SQL.
In MySQL/MariaDB, invocare il client da riga di comando ed utilizzare i comandi:

CREATE DATABASE groupbytestdb;
USE DATABASE groupbytestdb;

Per creare un database di test denominato ‘groupbytestdb’.
Per SQLite3, è sufficiente invocare il programma, anche senza argomenti.

Prescindendo dal particolare DBMS, ora è possibile creare la tabella T ed inserire tuple d’esempio:

CREATE TABLE T (
    A INTEGER,
    B INTEGER
);
INSERT INTO T (A, B) VALUES (1, 2);
INSERT INTO T (A, B) VALUES (1, 3);
INSERT INTO T (A, B) VALUES (2, 3);
INSERT INTO T (A, B) VALUES (2, 1);
INSERT INTO T (A, B) VALUES (3, 0);

Per testare GROUP BY con molteplici attributi, aggiungere una tupla duplicata:

INSERT INTO T (A, B) VALUES (1, 2);