4th Jul 2022 10 minutes de lecture Comment fonctionne la clause GROUP BY de SQL ? Ignacio L. Bisso sql apprendre sql group by Table des matières GROUP BY, 1ère partie : Regroupement des données GROUP BY, partie 2 : fonctions d'agrégation Regroupement d'enregistrements par plusieurs colonnes Regroupement des valeurs NULL Utilisation de WHERE avec GROUP BY Éviter les problèmes avec GROUP BY Problèmes de comptage Omettre les colonnes non agrégées de GROUP BY Il y a plus à faire avec GROUP BY Le regroupement des résultats est une fonction SQL puissante qui vous permet de calculer des statistiques clés pour un groupe d'enregistrements. GROUP BY est l'une des clauses les plus puissantes de SQL. Elle vous permet de voir les données d'une nouvelle manière et de trouver des mesures clés (comme la moyenne, les valeurs maximales et minimales dans un groupe d'enregistrements). Sans GROUP BY, tous les résultats que nous obtenons sont orientés vers les enregistrements. Avec GROUP BY, nous pouvons créer des groupes d'enregistrements et calculer des métriques sur chaque groupe. Dans cet article, vous apprendrez comment GROUP BY rend vos requêtes SQL beaucoup plus puissantes et diversifiées. GROUP BY, 1ère partie : Regroupement des données Supposons que nous ayons un petit hôtel en Patagonie. Nous disposons également d'une base de données contenant les noms des clients, leurs villes d'origine, leurs âges, leurs dates d'arrivée et de départ, etc. Ces données se trouvent dans deux tables appelées room_guest et guest. Regardez : 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 Nous voulons calculer certaines statistiques afin de pouvoir réserver davantage d'invités. La clause SQL GROUP BY nous permet de regrouper des enregistrements en fonction des données d'une ou plusieurs colonnes données. Nous pouvons regrouper les enregistrements de la table room_guest en fonction de la valeur de la colonne origin_city. Tous les enregistrements des clients de "Genova" appartiennent alors à un groupe, tous les enregistrements des clients de "Dubai" appartiennent à un autre groupe, et ainsi de suite. Le tableau suivant présente chaque groupe d'enregistrements dans une couleur différente. 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 Supposons maintenant que le propriétaire de l'hôtel souhaite savoir combien de clients viennent de chaque ville. Pour le savoir, nous devons compter le nombre d'enregistrements dans chaque groupe. En d'autres termes, nous avons besoin de la fonction d'agrégation COUNT(*), qui renvoie le nombre d'enregistrements dans un groupe. COUNT() est une fonction très courante ; nous y reviendrons plus tard dans cet article. Nous avons donc besoin d'une requête pour créer des groupes d'enregistrements ayant la même valeur dans origin_city et compter ensuite le nombre d'enregistrements dans chaque groupe. La requête ressemblerait à ceci : SELECT origin_city, COUNT(*) AS quantity_of_guests FROM room_guest GROUP BY origin_city Vous pouvez comparer le nombre d'invités de chaque ville dans le tableau de résultats ci-dessous avec le tableau coloré présenté précédemment : origin_cityquantity_of_guests Dubai3 Genova3 Los Angeles1 San Francisco5 San Pedro2 Remarquez que le nombre de lignes dans les résultats de la requête est le même que la quantité de groupes créés par la clause GROUP BY. Un groupe pour chaque ville, une ligne pour chaque ville. Pour conclure cette introduction à GROUP BY, je vous suggère de lire l'article Getting the Hang of the GROUP BY Clause. Il comprend une description complète de GROUP BY et plusieurs exemples de ses erreurs les plus courantes. GROUP BY, partie 2 : fonctions d'agrégation Si le regroupement par une valeur est pratique, la véritable puissance de GROUP BY réside dans son utilisation avec les fonctions d'agrégation. J'irais même jusqu'à dire que toute requête SQL utilisant une clause GROUP BY devrait comporter au moins une fonction d'agrégation. (Mais ce n'est pas obligatoire). Dans la section précédente, nous avons mentionné que GROUP BY est utilisé pour créer des groupes et calculer des métriques. Les métriques sont calculées par des fonctions d'agrégation comme COUNT(), SUM(), AVG(), MIN() et MAX(). Les valeurs calculées par chacune de ces fonctions sont explicites. Cependant, elles ont toutes quelque chose en commun : toutes les fonctions d'agrégation renvoient une valeur basée sur tous les enregistrements du groupe. Prenons un exemple. Le propriétaire d'un hôtel veut connaître la valeur maximale facturée pour chaque chambre. Il souhaite également connaître la valeur minimale et moyenne facturée pour chaque chambre. Voici la requête, suivie des résultats : 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 Regroupement d'enregistrements par plusieurs colonnes Dans certains cas, nous pouvons avoir besoin de regrouper les enregistrements par deux colonnes ou plus. Pouvons-nous le faire avec GROUP BY? Certainement ! Dans la requête précédente, nous avons créé un rapport analysant combien d'argent chaque chambre génère. Cependant, certaines chambres peuvent être configurées à un niveau premium ou standard (voir la chambre numéro 2002) pendant différentes saisons ; ainsi, pour effectuer une analyse correcte, nous devons regrouper les enregistrements en utilisant deux colonnes : room_number et room_level. Avant de passer à la requête, utilisons des couleurs pour voir comment les enregistrements sont regroupés par la clause GROUP BY room_number, room_level. Rappelez-vous que les enregistrements de chaque groupe doivent avoir exactement les mêmes valeurs dans room_number et room_level. Par exemple, le premier groupe concerne room_number = 1001 et 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 requête est la suivante : 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 Le tableau suivant montre les résultats de cette requête. Vous pouvez comparer ce tableau avec le tableau précédent pour vérifier les résultats. 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 Regroupement des valeurs NULL Comme toute autre valeur, les valeurs NULL ont leur propre groupe ; si nous avons un NULL dans l'une des colonnes de GROUP BY, un groupe supplémentaire d'enregistrements est créé pour ces enregistrements. Pour démontrer cela, nous devons insérer une paire d'enregistrements avec des valeurs NULL dans la colonne 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); Ensuite, cette requête ... SELECT origin_city, COUNT(*) AS quantity_of_guests FROM room_guest GROUP BY origin_city ... donnera le résultat suivant. Remarquez le nouveau groupe pour les valeurs NULL origin_city dans la première ligne : origin_cityquantity_of_guests NULL2 Dubai3 Genova3 Los Angeles1 San Francisco5 San Pedro2 Utilisation de WHERE avec GROUP BY La clause WHERE est fréquemment utilisée dans les requêtes SQL, il est donc important de comprendre comment elle fonctionne lorsqu'elle est combinée avec GROUP BY. La clause WHERE est appliquée avant la clause GROUP BY. Cela signifie que tous les enregistrements sont d'abord filtrés par WHERE; ensuite, les enregistrements qui correspondent à la condition WHERE sont regroupés à l'aide des critères GROUP BY. À titre d'exemple, utilisons la requête précédente, mais cette fois-ci, nous filtrons les invités provenant des villes de San Francisco et Los Angeles. La requête est la suivante : 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 Comme on pouvait s'y attendre, cet ensemble de résultats est plus court que les précédents ; la clause WHERE a filtré de nombreux invités, et seuls les enregistrements des chambres de San Francisco et Los Angeles ont été traités par la clause 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 Éviter les problèmes avec GROUP BY Lorsque vous commencez à utiliser GROUP BY, il est courant de rencontrer les problèmes suivants. Voici comment les éviter. Problèmes de comptage Examinons un cas similaire où nous devons ajouter plus d'une colonne supplémentaire dans la clause GROUP BY. Dans la première requête, nous avons regroupé les données par origin_city. Cependant, certaines villes portent le même nom (parce qu'elles se trouvent dans différents États ou pays). Dans notre ensemble de données, nous avons deux villes différentes nommées San Pedro, l'une en Argentine et l'autre en Espagne. Nous ne voulons pas les compter ensemble, car ce sont deux endroits différents. Pour compter ces villes séparément, nous devons regrouper les enregistrements en utilisant les colonnes city_origin, state et country. Nous allons ensuite répéter la première requête mais en ajoutant les colonnes state et country à la clause GROUP BY. Cependant, si nous ajoutons des colonnes à la clause GROUP BY, nous devons également les ajouter à la clause SELECT. Comme les colonnes état et pays sont dans la table guest nous devons JOIN les tables room_guest et guest. Voici la requête que nous avons : 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 Les résultats montrent deux villes "San Pedro" différentes car nous avons utilisé state et country comme colonnes supplémentaires dans la clause GROUP BY. origin_citystatecountrynumber_of_guests DubaiDubaiUAE3 GenovaLiguriaItaly3 Los AngelesCaliforniaUnited States1 San FranciscoCaliforniaUnited States5 San PedroBuenos AiresArgentina1 San PedroAndaluciaSpain1 Il reste un problème à résoudre dans cette requête : si la même personne a visité l'hôtel deux fois, nous la comptons deux fois. Ce n'est pas nécessairement faux, mais que faire si nous voulons connaître le nombre de visiteurs uniques de l'hôtel ? Nous devrions utiliser COUNT(distinct guest_name). La fonction de regroupement COUNT(distinct column) renvoie la quantité de valeurs uniques pour une colonne donnée dans un groupe d'enregistrements. Dans la requête ci-dessous, nous ajoutons la fonction COUNT(distinct). Nous conservons également la requête originale COUNT(*) afin que le lecteur puisse comparer les deux résultats : 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 Maintenant, nous pouvons voir que l'hôtel a reçu un total de trois visites d'un résident de Dubaï, mais que ces trois visites ont été faites par deux personnes distinctes (Peter S. et Olek V) . origin_citystatecountrynumber_of_unique_guestsnumber_of_guests DubaiDubaiUAE23 GenovaLiguriaItaly23 Los AngelesCaliforniaUnited States11 San FranciscoCaliforniaUnited States25 San PedroBuenos AiresArgentina11 San PedroAndaluciaSpain11 Avant de clore cette section, je vous suggère de regarder cette vidéo de 5 minutes sur GROUP BY pour les débutants. C'est une façon super dynamique d'apprendre SQL. Omettre les colonnes non agrégées de GROUP BY Une autre erreur très courante de GROUP BY est d'ajouter une colonne non agrégée (c'est-à-dire une colonne qui n'est pas utilisée dans une fonction d'agrégation) dans SELECT que vous n'avez pas dans GROUP BY. Pour éviter cette erreur, suivez une règle très simple : Toutes les colonnes de SELECT doivent apparaître dans la clause GROUP BY ou être utilisées dans une fonction d'agrégation. Essayons une requête invalide pour voir l'erreur : 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 Si nous exécutons cette requête, nous obtiendrons l'erreur suivante : ERROR: The column «room_guest.origin_city» must be in the GROUP BY clause LINE 3: guest_age, Nous pouvons corriger cette erreur en ajoutant la colonne origin_city à la clause 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 Si vous essayez de comprendre la différence entre GROUP BY et ORDER BY, lisez la différence entre GROUP BY et ORDER BY en termes simples. Elle vous aidera à y voir plus clair. Il y a plus à faire avec GROUP BY Nous avons donc appris à utiliser GROUP BY pour regrouper des enregistrements par valeurs communes. Nous savons que les fonctions d'agrégation MIN(), MAX(), AVG() et SUM() permettent de calculer diverses statistiques. Et la fonction COUNT() fait beaucoup de choses : COUNT(*) compte toutes les lignes. COUNT(guest_name) compte toutes les valeurs non NULL dans la colonne guest_name. COUNT(distinct guest_name) compte toutes les valeurs différentes non NULL dans la colonne guest_name. Lors du regroupement, NULL obtient son propre groupe. Et toutes les colonnes non agrégées de SELECT doivent être présentes dans GROUP BY. En raison de la longueur de l'article, je n'ai pas couvert la clause HAVING, qui est une sorte de clause WHERE utilisée pour filtrer les groupes au lieu des enregistrements. Pour les lecteurs qui souhaitent aller un peu plus loin, je vous laisse un lien vers notre SQL pour les débutants qui couvre de nombreux sujets intéressants. C'est un excellent moyen de renforcer vos compétences en SQL ! Tags: sql apprendre sql group by