20th Jul 2022 Tempo di lettura: 10 minuti Come funziona il GROUP BY in SQL? Ignacio L. Bisso sql imparare sql GROUP BY Indice GROUP BY, parte 1: Raggruppare i dati GROUP BY, parte 2: funzioni di aggregazione Raggruppare i record in base a più colonne Raggruppamento dei valori NULL Utilizzo di WHERE con GROUP BY Evitare i problemi con GROUP BY Problemi di conteggio Omissione di colonne non aggregate da GROUP BY C'è di più da fare con GROUP BY Il raggruppamento dei risultati è una potente funzione di SQL che consente di calcolare statistiche chiave per un gruppo di record. GROUP BY è una delle clausole più potenti di SQL. Permette di vedere i dati in modo nuovo e di trovare metriche chiave (come la media, i valori massimi e minimi in un gruppo di record). Senza GROUP BY, tutti i risultati ottenuti sono orientati ai record. Con GROUP BY, possiamo creare gruppi di record e calcolare le metriche su ciascun gruppo. In questo articolo scoprirete come GROUP BY rende le vostre query SQL molto più potenti e diversificate. GROUP BY, parte 1: Raggruppare i dati Supponiamo di avere un piccolo hotel in Patagonia. Abbiamo anche un database contenente i nomi degli ospiti, le città di provenienza, l'età, le date di check-in, le date di check-out e altro ancora. Questi dati sono contenuti in due tabelle chiamate room_guest e guest. Date un'occhiata: room_guest guest_nameorigin_cityroom_numberday_inday_outageroom_levelamount_invoiced Juan B.San Pedro10012012-12-282013-01-0732standard$9500 Mary J.San Francisco10022013-01-022013-01-1223standard$6700 Peter S.Dubai20022013-01-022013-01-2965premium$34000 Clair BGenova20012014-07-022014-08-0221standard$16000 Meiling Y.San Francisco20022014-11-022014-11-1252standard$9500 Olek V.Dubai20032015-01-022015-01-3137premium$28400 Benjamin L.San Pedro20022016-01-022016-01-1561premium$15400 Arnaldo V.Genova10012017-01-012017-01-0443standard$2500 Mary J.San Francisco10022017-01-022017-01-0723standard$4800 Wei W.Los Angeles20022018-01-022018-01-2231standard$12000 Meiling Y.San Francisco20012018-01-022018-01-2252premium$17500 Peter S.Dubai20022019-01-022019-02-2565premium$32000 Arnaldo V.Genova20032019-08-052019-08-1743standard$11200 Mary J.San Francisco10012019-01-022019-01-1223standard$8900 guest guest_namepreferred_activitycity_namestatecountrycontinent activityCity_nameStateCountryContinent32 Juan B.trekkingSan PedroAndaluciaSpainEurope Mary J.trekkingSan FranciscoCaliforniaUnited StatesAmerica Peter S.trekkingDubaiDubaiArabiaAsia Chiara BskiingGenovaLiguriaItalyEurope Meiling Y.trekkingSan FranciscoCaliforniaUnited StatesAmerica Olek V.relaxingDubaiDubaiArabiaAsia Benjamin L.skiingSan PedroBuenos AiresArgentinaAmerica Wei W.trekkingLos AngelesCaliforniaUnited StatesAmerica Arnaldo V.skiingGenovaLiguriaItalyEurope Vogliamo calcolare alcune statistiche per poter prenotare più ospiti. La clausola SQL GROUP BY consente di raggruppare i record in base ai dati di una o più colonne. Possiamo raggruppare i record della tabella room_guest in base al valore della colonna origin_city. In questo modo tutti i record degli ospiti di "Genova" apparterranno a un gruppo; tutti i record degli ospiti di "Dubai" apparterranno a un altro gruppo e così via. La tabella seguente mostra ogni gruppo di record con un colore diverso. guest_nameorigin_cityroom_numberday_inday_outageroom_levelamount_invoiced Peter S.Dubai20022013-01-022013-01-2965premium$34000 Olek V.Dubai20032015-01-022015-01-3137premium$28400 Peter S.Dubai20022019-01-022019-02-2565premium$32000 Clair BGenova20012014-07-022014-08-0221standard$16000 Arnaldo V.Genova10012017-01-012017-01-0443standard$2500 Arnaldo V.Genova20032019-08-052019-08-1743standard$11200 Wei W.Los Angeles20022018-01-022018-01-2231standard$12000 Mary J.San Francisco10022013-01-022013-01-1223standard$6700 Mary J.San Francisco10022017-01-022017-01-0723standard$4800 Meiling Y.San Francisco20022014-11-022014-11-1252standard$9500 Meiling Y.San Francisco20012018-01-022018-01-2256premium$17500 Mary J.San Francisco10012019-01-022019-01-1223standard$8900 Benjamin L.San Pedro20022016-01-022016-01-1561premium$15400 Juan B.San Pedro10012012-12-282013-01-0732standard$9500 Supponiamo che il proprietario dell'hotel voglia sapere quanti ospiti provengono da ogni città. Per scoprirlo, dobbiamo contare il numero di record di ciascun gruppo. In altre parole, abbiamo bisogno della funzione aggregata COUNT(*), che restituisce il numero di record in un gruppo. COUNT() è una funzione molto comune; ci torneremo più avanti in questo articolo. Quindi, abbiamo bisogno di una query per creare gruppi di record con lo stesso valore in origin_city e poi contare il numero di record in ogni gruppo. La query dovrebbe essere simile a questa: SELECT origin_city, COUNT(*) AS quantity_of_guests FROM room_guest GROUP BY origin_city È possibile confrontare il numero di ospiti di ciascuna città nella tabella dei risultati sottostante con la tabella colorata mostrata in precedenza: origin_cityquantity_of_guests Dubai3 Genova3 Los Angeles1 San Francisco5 San Pedro2 Notate che il numero di righe nei risultati della query è uguale alla quantità di gruppi creati dalla clausola GROUP BY. Un gruppo per ogni città, una riga per ogni città. Per concludere questa introduzione a GROUP BY, vi suggerisco di leggere l'articolo Come utilizzare la clausola GROUP BY. L'articolo contiene una descrizione completa della clausola GROUP BY e diversi esempi degli errori più comuni. GROUP BY, parte 2: funzioni di aggregazione Sebbene il raggruppamento in base a un valore sia utile, la vera potenza di GROUP BY è quando viene utilizzato con le funzioni di aggregazione. Direi che ogni query SQL che utilizza una clausola GROUP BY dovrebbe avere almeno una funzione aggregata. (Ma non è obbligatorio). Nella sezione precedente, abbiamo detto che GROUP BY è usato per creare gruppi e calcolare metriche. Le metriche sono calcolate da funzioni di aggregazione come COUNT(), SUM(), AVG(), MIN() e MAX(). I valori calcolati da ciascuna di queste funzioni sono autoesplicativi. Tuttavia, tutte hanno qualcosa in comune: tutte le funzioni di aggregazione restituiscono un valore basato su tutti i record del gruppo. Consideriamo un esempio. Il proprietario di un hotel vuole conoscere il valore massimo fatturato per ogni camera. Oltre a questo, vuole vedere il valore minimo e medio fatturato per ogni camera. Ecco la query e i risultati: SELECT room_number, MAX(amount_invoiced) AS max_amount_invoiced, MIN(amount_invoiced) AS min_amount_invoiced, AVG(amount_invoiced) AS average_amount_invoiced FROM room_guest GROUP BY room_number room_numbermax_amount_invoicedmin_amount_invoicedaverage_amount_invoiced 10019500.002500.006966.66 10026700.004800.005750.00 200117500.0016000.0016750.00 200234000.009500.0020580.00 200328400.0011200.0019800.00 Raggruppare i record in base a più colonne In alcuni casi, potrebbe essere necessario raggruppare per due o più colonne. Possiamo farlo con GROUP BY? Certo che sì! Nella query precedente, abbiamo creato un report che analizza la quantità di denaro generata da ciascuna camera. Tuttavia, alcune camere possono essere configurate a livello premium o standard (vedere il numero di camera 2002) durante le diverse stagioni; quindi, per effettuare un'analisi corretta, dobbiamo raggruppare i record utilizzando due colonne: room_number e room_level. Prima di passare alla query, utilizziamo i colori per vedere come vengono raggruppati i record in base alla clausola GROUP BY room_number, room_level. Ricordate che i record di ogni gruppo devono avere esattamente gli stessi valori in room_number e room_level. Ad esempio, il primo gruppo è per room_number = 1001 e room_level = ‘standard’. guest_nameorigin_cityroom_numberday_inday_outageroom_levelamount_invoiced Mary J.San Francisco10012019-01-022019-01-1223standard$8900 Arnaldo V.Genova10012017-01-012017-01-0443standard$2500 Juan B.San Pedro10012012-12-282013-01-0732standard$9500 Mary J.San Francisco10022013-01-022013-01-1223standard$6700 Mary J.San Francisco10022017-01-022017-01-0723standard$4800 Meiling Y.San Francisco20012018-01-022018-01-2252premium$17500 Clair BGenova20012014-07-022014-08-0221standard$16000 Benjamin L.San Pedro20022016-01-022016-01-1561premium$15400 Peter S.Dubai20022013-01-022013-01-2965premium$34000 Peter S.Dubai20022019-01-022019-02-2565premium$32000 Meiling Y.San Francisco20022014-11-022014-11-1252standard$9500 Wei W.Los Angeles20022018-01-022018-01-2231standard$12000 Olek V.Dubai20032015-01-022015-01-3137premium$28400 Arnaldo V.Genova20032019-08-052019-08-1743standard$11200 La query è: SELECT room_number, room_level, MAX(amount_invoiced) AS max_amount_invoiced, MIN(amount_invoiced) AS min_amount_invoiced, AVG(amount_invoiced) AS average_amount_invoiced FROM room_guest GROUP BY room_number, room_level La tabella seguente mostra i risultati di questa query. È possibile confrontare questa tabella con quella precedente per verificare i risultati. room_numberroom_levelmax_amount_invoicedmin_amount invoicedaverage_amoun_invoiced 1001standard9500.002500.006966.66 1002standard6700.004800.005750.00 2001premium17500.0017500.0017500.00 2001standard16000.0016000.0016000.00 2002premium34000.0015400.0027133.33 2002standard12000.009500.0010750.00 2003premium28400.0028400.0028400.00 2003standard11200.0011200.0011200.00 Raggruppamento dei valori NULL Come qualsiasi altro valore, anche i valori di NULL hanno un loro gruppo; se abbiamo un NULL in una qualsiasi colonna di GROUP BY, viene creato un gruppo di record in più per quei record. Per dimostrarlo, occorre inserire una coppia di record con valori NULL nella colonna origin_city: INSERT INTO into room_guest VALUES ('Kevin C.', NULL, 2001, '2019-07-25', '2019-08-07', NULL, 'standard', 10500); INSERT INTO into room_guest VALUES ('Karl J.', NULL, 1002, '2019-11-12', '2019-11-22', NULL, 'premium', 13900); Quindi, questa query ... SELECT origin_city, COUNT(*) AS quantity_of_guests FROM room_guest GROUP BY origin_city ... mostrerà il seguente risultato. Si noti il nuovo gruppo per i valori NULL di origin_city nella prima riga: origin_cityquantity_of_guests NULL2 Dubai3 Genova3 Los Angeles1 San Francisco5 San Pedro2 Utilizzo di WHERE con GROUP BY La clausola WHERE è usata spesso nelle query SQL, quindi è importante capire come funziona quando è combinata con GROUP BY. La clausola WHERE viene applicata prima di GROUP BY. Ciò significa che tutti i record vengono filtrati prima da WHERE; quindi i record che corrispondono alla condizione WHERE vengono raggruppati utilizzando i criteri GROUP BY. Come esempio, utilizziamo la query precedente, ma questa volta filtreremo gli ospiti provenienti dalle città di San Francisco e Los Angeles. La query è: SELECT room_number, room_level, MAX(amount_invoiced) AS max_amount_invoiced, MIN(amount_invoiced) AS min_amount_invoiced, AVG(amount_invoiced) AS average_amount_invoiced FROM room_guest WHERE origin_city IN (‘San Francisco’,’Los Angeles’ ) GROUP BY room_number, room_level Come ci si aspettava, questo insieme di risultati è più breve di quelli precedenti; la clausola WHERE ha filtrato molti ospiti e solo i record delle camere di San Francisco e Los Angeles sono stati elaborati dalla clausola GROUP BY. room_numberroom_levelmax_amount_invoicedmin_amount_ invoicedaverage_amount_invoiced 1001standard8900.008900.008900.00 1002standard6700.004800.005750.00 2001premium17500.0017500.0017500.00 2002standard12000.009500.0010750.00 Evitare i problemi con GROUP BY Quando si inizia a usare la clausola GROUP BY, è comune imbattersi nei seguenti problemi. Ecco come evitarli. Problemi di conteggio Esaminiamo un caso simile in cui è necessario aggiungere più di una colonna supplementare alla clausola GROUP BY. Nella prima query, abbiamo raggruppato per origin_city. Tuttavia, alcune città hanno lo stesso nome (perché si trovano in stati o paesi diversi). Nel nostro set di dati, abbiamo due diverse città di nome San Pedro, una in Argentina e l'altra in Spagna. Non vogliamo contarle insieme, perché sono due luoghi diversi. Per contare queste città separatamente, dobbiamo raggruppare i record utilizzando le colonne city_origin, state e country. Quindi ripeteremo la prima query, ma aggiungendo le colonne state e country alla clausola GROUP BY. Tuttavia, se aggiungiamo delle colonne alla clausola GROUP BY, dobbiamo aggiungerle anche alla clausola SELECT. Poiché le colonne Stato e Paese sono presenti nella tabella guest è necessario JOIN le tabelle room_guest e guest. Ecco la query che abbiamo: SELECT origin_city, state, country COUNT(*) AS number_of_guests FROM room_guest JOIN guest ON guest.guest_name = room_guest.guest_name GROUP BY origin_city, state, country I risultati mostrano due diverse città "San Pedro", perché abbiamo usato state e country come colonne aggiuntive nella clausola GROUP BY. origin_citystatecountrynumber_of_guests DubaiDubaiUAE3 GenovaLiguriaItaly3 Los AngelesCaliforniaUnited States1 San FranciscoCaliforniaUnited States5 San PedroBuenos AiresArgentina1 San PedroAndaluciaSpain1 C'è ancora un problema da risolvere in questa query: se la stessa persona ha visitato l'hotel due volte, la stiamo contando due volte. Questo non è necessariamente sbagliato, ma se volessimo conoscere il numero di visitatori unici dell'hotel? Dovremmo usare COUNT(distinct guest_name). La funzione di raggruppamento COUNT(distinct column) restituisce la quantità di valori unici per una determinata colonna in un gruppo di record. Nella query seguente, aggiungiamo la funzione COUNT(distinct). Manteniamo anche la funzione originale COUNT(*), in modo che il lettore possa confrontare i due risultati: SELECT origin_city, state, country COUNT(distinct guest_name) AS number_of_unique_guests, COUNT(*) AS number_of_guests FROM room_guest JOIN guest ON guest.guest_name = room_guest.guest_name GROUP BY origin_city, state, country Ora possiamo vedere che l'hotel ha ricevuto un totale di tre visite da un residente di Dubai, ma che queste tre visite sono state effettuate da due persone distinte (Peter S. e Olek V) . origin_citystatecountrynumber_of_unique_guestsnumber_of_guests DubaiDubaiUAE23 GenovaLiguriaItaly23 Los AngelesCaliforniaUnited States11 San FranciscoCaliforniaUnited States25 San PedroBuenos AiresArgentina11 San PedroAndaluciaSpain11 Prima di chiudere questa sezione, vi suggerisco di guardare questo video di 5 minuti su GROUP BY per i principianti. È un modo super dinamico per imparare SQL. Omissione di colonne non aggregate da GROUP BY Un altro errore molto comune di GROUP BY è quello di aggiungere una colonna non aggregata (cioè una colonna non utilizzata in una funzione aggregata) in SELECT che non è presente in GROUP BY. Per evitare questo errore, seguite una regola molto semplice: Tutte le colonne in SELECT devono comparire nella clausola GROUP BY o essere utilizzate in una funzione aggregata. Proviamo a eseguire una query non valida per vedere l'errore: SELECT room_number, room_level, origin_city, --This column is invalid, is not in the GROUP BY COUNT(*) AS quantity_of_visitors, FROM room_guest GROUP BY room_number, room_level Se eseguiamo questa query, otterremo il seguente errore: ERROR: The column «room_guest.origin_city» must be in the GROUP BY clause LINE 3: guest_age, Possiamo risolvere l'errore aggiungendo la colonna origin_city alla clausola GROUP BY: SELECT room_number, room_level, Origin_city, COUNT(*) AS quantity_of_visitors FROM room_gest GROUP BY room_number, room_level, origin_city -- origin_city added Se state cercando di capire la differenza tra GROUP BY e ORDER BY, leggete Differenza tra GROUP BY e ORDER BY in parole semplici. Vi aiuterà a capire come funziona. C'è di più da fare con GROUP BY Abbiamo imparato a usare GROUP BY per raggruppare i record in base a valori comuni. Sappiamo che le funzioni aggregate MIN(), MAX(), AVG() e SUM() calcolano varie statistiche. La funzione COUNT() fa molte cose: COUNT(*) conta tutte le righe. COUNT(guest_name) conta tutti i valori non NULL nella colonna nome_ospite. COUNT(distinct guest_name) conta tutti i valori diversi non NULL nella colonna guest_name. Quando si raggruppa, NULL riceve il proprio gruppo. Tutte le colonne non aggregate in SELECT devono essere presenti in GROUP BY. A causa della lunghezza dell'articolo, non ho trattato la clausola HAVING, che è una sorta di clausola WHERE utilizzata per filtrare gruppi anziché record. Per i lettori che vogliono fare un passo avanti, lascio un link al nostro corso SQL Basics che tratta molti argomenti interessanti. È un ottimo modo per sviluppare le proprie competenze in SQL! Tags: sql imparare sql GROUP BY