4th Jul 2022 14 minutes de lecture 7 erreurs courantes de GROUP BY Marija Ilic sql apprendre sql group by Table des matières Ne commettez plus ces 7 erreurs courantes de GROUP BY 1. Oublier GROUP BY avec les fonctions d'agrégation 2. Confusion entre WHERE et HAVING Exemple 1 - Comment afficher les catégories de repas avec 1M+ de vues Exemple 2 - Performances de Jean dans chaque catégorie de repas 3. Énumération d'une colonne dans SELECT mais pas dans GROUP BY 4. Pas de regroupement par une clé unique 5. Confusion entre COUNT(distinct) et COUNT(*) 6. Problèmes d'utilisation des fonctions d'agrégation avec des valeurs NULL 7. Utilisation de COUNT(*) avec GROUP BY et LEFT JOIN Vous pouvez résoudre les erreurs GROUP BY ! Vous commettez ces erreurs avec GROUP BY en SQL ? Découvrez ce qu'elles sont, comment les éviter et comment les corriger. L'instruction SQL GROUP BY peut permettre de découvrir rapidement des informations importantes sur les données. Au début, l'utilisation de GROUP BY peut sembler facile, par exemple lors de la création de rapports SQL de base que vous présenterez aux décideurs de l'entreprise. Mais en apprenant cette puissante fonctionnalité, vous pourriez vous retrouver piégé par des erreurs bizarres ou obtenir des résultats incorrects causés par des instructions GROUP BY mal rédigées. Si vous avez l'impression que les choses ne tournent pas rond dans votre utilisation de GROUP BY, poursuivez votre lecture. Dans cet article, je vais vous expliquer les erreurs les plus courantes de GROUP BY et comment vous pouvez les éviter. Ne commettez plus ces 7 erreurs courantes de GROUP BY 1. Oublier GROUP BY avec les fonctions d'agrégation Vous utilisez les instructions SELECT avec la clause GROUP BY lorsque vous souhaitez regrouper et organiser des lignes en groupes spécifiques, puis effectuer un calcul spécifique sur chaque groupe. L'erreur la plus courante de GROUP BY est d'oublier d'écrire GROUP BY à l'intérieur de l'instruction SELECT. Voici un exemple. Imaginez que vous avez la table recettesqui contient 100 enregistrements et six colonnes. Cette table stocke le nombre de vues (no_of_views) pour chaque recette publiée sur un célèbre site web culinaire : meal_categorynameauthorno_of_viewsno_of_views_lst_mthauthor_id Cold appetizerMarinated CheeseMarta107104906211 SoupsPumpkin soupJohn68856693772 dessertsBanana CheesecakeAlly131944NULL3 drinksPaloma PicanteLuke72027713124 Bread and pastrySour Cream DoughnutsJohn50935527912 dessertsReal Strawberry CupcakesLisa17626811693911 Soupspotato soupMary64796643886 .................. .................. .................. Bread and pastryCider DoughnutsTim53896511608 tableau des recettes Voici une brève description des colonnes de ce tableau : meal_category - La catégorie de la recette (soupe, boissons, desserts, etc.). name - Le nom de la recette. author - Le nom de l'auteur. no_of_views - Le nombre de vues (total des pages/recettes vues) dans le mois en cours. no_of_views_lst_mth - Le nombre de consultations (total des pages/recettes consultées) au cours du mois précédent. author_id - Le numéro d'identification unique de l'auteur. Supposons que vous souhaitiez compter le nombre de recettes dans chaque catégorie de repas. Si vous écrivez la déclaration comme ceci (sans GROUP BY à la fin) ... SELECT meal_category, count(*) AS total_recipes FROM recipes ; ... la plupart des moteurs SQL vous donneront une erreur. Certains moteurs, cependant, produiront des résultats bizarres et indésirables. J'utilise MySQL et lorsque j'exécute cette instruction, j'obtiens ceci : meal_categorytotal_recipes Cold appetizer100 Résultat sans GROUP BY 100 est le nombre total de recettes dans l'ensemble des données et la catégorie de repas "Entrée froide" n'est qu'une catégorie sur dix. Pour corriger ce type d'erreur, vous devez ajouter un GROUP BY meal_category à la fin de l'instruction. (Sinon, votre résultat dans MySQL n'a tout simplement aucun sens). Le SELECT correct ressemble à ceci : SELECT meal_category, count(*) AS total_recipes FROM recipes GROUP BY meal_category ; Voici une brève explication de ce qui se passe : Les enregistrements sont fusionnés en fonction de la catégorie de repas. Par exemple, les desserts constituent un groupe, les soupes un autre, les plats principaux un autre encore, etc. La colonne meal_category est spécifiée après GROUP BY; elle figure également dans SELECT. Pour chaque groupe, nous utilisons COUNT(*) pour compter le nombre total de recettes dans ce groupe. Je ne vais pas m'attarder sur la syntaxe ici, mais je vous conseille vivement de lire Explication de GROUP BY en SQL ou Utilisation de GROUP BY en SQL pour plus de détails. Comme vous pouvez le constater, le résultat est conforme à nos attentes : meal_categorytotal_recipes Bread and pastry7 Cold appetizer6 desserts20 drinks7 Main dishes20 Salads8 Side dishes12 Soups17 Warm appetizer3 Résultat valide de GROUP BY 2. Confusion entre WHERE et HAVING Vous aimeriez peut-être voir uniquement les catégories de repas qui contiennent plus de 10 recettes. Beaucoup de débutants écriraient cette requête : SELECT meal_category, count(*) AS total_recipes FROM recipes WHERE count(*) > 10 GROUP BY meal_category ; Cette instruction renvoie une erreur car vous ne pouvez pas utiliser de fonctions d'agrégation dans une clause WHERE. WHERE est utilisé avec GROUP BY lorsque vous souhaitez filtrer les lignes avant de les regrouper. Dans notre exemple, nous voulons filtrer les lignes après les avoir regroupées; dans des cas comme celui-ci, nous devons utiliser la clause HAVING: SELECT meal_category, count(*) AS total_recipes FROM recipes GROUP BY meal_category HAVING count(*) > 10 ; Ce malentendu sur la différence entre HAVING et WHERE est la deuxième erreur la plus courante avec GROUP BY. Clarifions cette différence à l'aide de deux autres exemples. Exemple 1 - Comment afficher les catégories de repas avec 1M+ de vues Une déclaration qui affiche uniquement les catégories ayant plus d'un million de pages vues au total peut être écrite comme ceci : SELECT meal_category, sum(no_of_views) AS total FROM recipes GROUP BY meal_category HAVING sum(no_of_views) >1000000; Ici, nous utilisons HAVING parce que nous voulons filtrer les enregistrements après qu'ils aient été regroupés. Le résultat est présenté ci-dessous : meal_categorytotal desserts2969324 Main dishes1323981 Side dishes1662910 Soups1100911 Exemple avec HAVING Exemple 2 - Performances de Jean dans chaque catégorie de repas Cette requête extrait uniquement les recettes de Jean et calcule ses performances : SELECT meal_category, sum(no_of_views) AS total FROM recipes WHERE author = ‘John’ GROUP BY meal_category; Nous utilisons WHERE parce que nous devons filtrer les enregistrements (afin de n'obtenir que les données de John) avant de placer les enregistrements dans des groupes par catégorie de repas. Voici à quoi ressemble le résultat : meal_categorytotal Bread and pastry50935 desserts301869 drinks147745 Main dishes279934 Salads88097 Side dishes415864 Soups393253 Warm appetizer85570 Les KPIs de John HAVING et WHERE sont joliment décrits dans nos articles What Is the Difference Between WHERE and HAVING Clauses in SQL ? et 5 Exemples de GROUP BY. Si vous souhaitez voir plus d'exemples sur ce sujet, je vous suggère de commencer par là. 3. Énumération d'une colonne dans SELECT mais pas dans GROUP BY Supposons maintenant que vous vouliez voir le nombre total de vues par meal_category et author. Nous pouvons le faire - ?il suffit d'ajouter la colonne auteur à notre requête précédente : SELECT meal_category, author, sum(no_of_views) AS total FROM recipes GROUP BY meal_category; Bien sûr que non, cela entraînera une erreur dans la plupart des moteurs SQL. Par exemple, Oracle vous dira "erreur: Not a GROUP BY expression". Pourquoi cette erreur déroutante ? Que manque-t-il ici ? Eh bien, le moteur SQL ne sait pas comment calculer le total pour chaque auteur parce que nous ne l'avons pas inclus dans la clause GROUP BY ; l'attribut author ne figure pas dans la clause GROUP BY. C'est une autre erreur courante avec GROUP BY. Corrigeons cette requête et exécutons-la une nouvelle fois : SELECT meal_category, author, sum(no_of_views) AS total FROM recipes GROUP BY meal_category, author; Le résultat est le suivant : meal_categoryauthortotal Bread and pastryDino53789 Bread and pastryJohn50935 Bread and pastryMarta52998 Bread and pastryMary52904 Bread and pastryPatricia51451 Bread and pastryTim106226 ......... ......... ......... SoupsMary125731 SoupsMonte128356 SoupsPatricia255574 SoupsTim132532 Warm appetizerJohn85570 Warm appetizerLisa82960 Warm appetizerMary87560 Maintenant, cela semble correct. N'oubliez pas que les colonnes non agrégées qui figurent dans SELECT doivent également figurer dans .GROUP BYDans notre cas, les colonnes non agrégées sont meal_category et author, qui sont maintenant dans SELECT et GROUP BY. Vous ne répertoriez pas les colonnes qui se trouvent dans les fonctions d'agrégation dans GROUP BY. Dans notre exemple, la colonne no_of_views est utilisée dans la fonction d'agrégation SUM() et n'est donc pas répertoriée dans la clause GROUP BY. Si vous souhaitez en savoir plus sur ce sujet, consultez notre article intitulé GROUP BY Clause : How Well Do You Know It ? Il explique pourquoi les colonnes SÉLECTÉES doivent apparaître dans la clause GROUP BY. En outre, l'article Comment corriger une erreur "Not a GROUP BY Expression" donne d'autres exemples liés à ce type d'erreur. 4. Pas de regroupement par une clé unique Essayons maintenant autre chose. Supposons que nous voulions obtenir le nombre moyen de pages vues pour chaque auteur de recette. La requête suivante calcule le nombre total moyen de pages vues pour chaque auteur en utilisant le nom de l'auteur : SELECT author, avg(no_of_views) FROM recipes GROUP BY author; En regardant le résultat, vous remarquerez que Lisa a une moyenne de 116101,5 pages vues : authoravg(NO_OF_VIEWS) Ally106545 Dino94667.9091 John88163.35 Lisa116101.5 Luke104591 Marta119789.1667 Mary101040.0588 Monte84794 Patricia81911.1333 Tim76185.375 GROUP BY author - mais les noms ne sont pas uniques Cependant, nous avons en fait deux auteurs nommés Lisa dans notre tableau. Lorsque nous regroupons les résultats par la colonne auteur, les deux Lisa obtiennent la même moyenne. Pourquoi ? Parce que nous utilisons une colonne non unique sur le site GROUP BY. Cela signifie que toutes les valeurs de regroupement ne doivent pas être uniques. Si nous voulons voir la moyenne de chaque Lisa séparément, nous devons ajouter author_id (une colonne unique) à la liste GROUP BY: SELECT author, author_id avg(no_of_views) FROM recipes GROUP BY author, author_id; Nous voyons maintenant que les recettes de Lisa(id=11) sont beaucoup plus consultées que les recettes de Lisa(id=5) : authorauthor_idavg(no_of_views) Ally3106545 Dino794667.9091 John288163.35 Lisa585798 Lisa11146405 Luke4104591 Marta1119789.1667 Mary6101040.0588 Monte984794 Patricia1081911.1333 Tim876185.375 GROUP BY avec auteur et author_id Il est important de toujours penser aux clés de regroupement. Les valeurs de regroupement doivent être uniques et doivent représenter chaque groupe de la manière souhaitée. Sinon, vous obtiendrez des résultats imprécis et confus, voire une erreur GROUP BY. 5. Confusion entre COUNT(distinct) et COUNT(*) Si vous êtes curieux de connaître le nombre total d'auteurs pour chaque catégorie de repas, vous pouvez écrire une instruction GROUP BY pour le calculer. Utilisons COUNT(*) et récupérons le nombre d'auteurs dans chaque catégorie : SELECT meal_category, count(*) FROM recipes GROUP BY meal_category; Voici le résultat - mais ce n'est pas ce que vous attendiez, n'est-ce pas ? meal_categorycount(*) Bread and pastry7 Cold appetizer6 desserts20 drinks7 Main dishes20 Salads8 Side dishes12 Soups17 Warm appetizer3 Il s'agit du nombre total de recettes dans chaque catégorie, et non du nombre total d'auteurs. Comment cela se fait-il ? Eh bien, COUNT(*) compte toutes les lignes de chaque groupe. La table recette contient des informations sur un recipe chaque enregistrement correspond à une recette. Cette requête compte les recettes (lignes) dans chaque catégorie, pas les auteurs des recettes. Un auteur peut avoir plusieurs recettes dans chaque catégorie. Pour obtenir les informations souhaitées, vous devez donc compter les auteurs distincts (en utilisant COUNT(distinct author) au lieu de COUNT(*)) dans chaque groupe. Il s'agit d'une erreur très courante de GROUP BY. Alors, quand devez-vous utiliser COUNT(*), COUNT(expression) et COUNT(distinct expression)? Prenons un exemple : SELECT meal_category, count(distinct author), count(author), count(*) FROM recipes GROUP BY meal_category; meal_categorycount(distinct author)count(author)count(*) Bread and pastry677 Cold appetizer266 desserts82020 drinks577 Main dishes92020 Salads688 Side dishes81212 Soups61717 Warm appetizer333 La différence entre COUNT(*) et COUNT(expression) est visible si nous effectuons des calculs sur une colonne qui comporte des valeurs manquantes. Lorsque des valeurs manquantes sont présentes, COUNT(*) comptera tous les enregistrements d'un groupe et COUNT(expression) ne comptera que les valeurs non nulles. Dans l'exemple ci-dessus, COUNT(*) et COUNT(author) donnent exactement le même résultat car la colonne auteur ne comporte pas de valeurs NULL. COUNT(distinct author) nous donne le nombre d'auteurs distincts pour chaque catégorie, ce qui n'est pas la même chose que COUNT(*). Par exemple, la catégorie repas apéritif froid contient six recettes de deux auteurs distincts. COUNT(*) compte le nombre de recettes (enregistrements) dans chaque catégorie, tandis que COUNT(distinct author) compte le nombre d'auteurs distincts. Donc, si vous souhaitez afficher le nombre total d'auteurs distincts pour chaque catégorie de repas, utilisez COUNT(distinct author). Voici la requête correcte : SELECT meal_category, count(distinct author) FROM recipes GROUP BY meal_category; GROUP BY meal_category; Pour une explication plus détaillée, voir Quelle est la différence entre COUNT(*), COUNT(1), COUNT(nom de colonne), et COUNT(DISTINCT nom de colonne) ? 6. Problèmes d'utilisation des fonctions d'agrégation avec des valeurs NULL Voici un autre problème de "valeur manquante". Disons que vous voulez calculer le nombre total moyen de vues du mois précédent pour chaque catégorie. Votre collègue a calculé ces chiffres, mais il aimerait que vous vérifiiez le résultat. Voici votre requête : SELECT meal_category, avg(no_of_views_lst_mth) as average, FROM recipes GROUP BY meal_category; Et ce que vous obtenez est ... meal_categoryaverage Bread and pastry52274.8571 Cold appetizer95584.2 desserts144349.7222 drinks72551.7143 Main dishes61350.8889 Salads90798.875 Side dishes139765.25 Soups64978.8824 Warm appetizer78390.6667 Le résultat semble correct et vous êtes confiant quant à l'exactitude de votre requête. Cependant, votre collègue a obtenu des chiffres légèrement différents : meal_categoryaverage Bread and pastry52274.8571 Cold appetizer79653.5 desserts129914.75 drinks72551.7143 Main dishes55215.8 Salads90798.875 Side dishes139765.25 Soups64978.8824 Warm appetizer78390.6667 Que s'est-il passé ? Pourquoi ces résultats différents ? En bref, les différents résultats proviennent d'interprétations différentes des valeurs manquantes. La colonne no_of_views_lst_mth représente le nombre total de pages vues au cours du mois précédent. Si une recette a été créée pendant le mois en cours, cette colonne sera NULL pour cette ligne. Par exemple, la recette du Gâteau au fromage à la banane d'Ally a été écrite pendant le mois en cours, il n'y a donc pas de statistiques pour le mois précédent : meal_categorynameauthorno_of_viewsno_of_views_lst_mthauthor_id dessertsBanana CheesecakeAlly131944NULL3 Le gâteau au fromage à la banane a été publié dans le mois en cours. Revenons maintenant à ces moyennes et à leurs différents résultats. Les moyennes sont calculées comme la somme totale de no_of_views_lst_mth divisée par le nombre total d'enregistrements. Si vous utilisez la fonction AVG() et que des NULL sont présents, le moteur ignore simplement les NULL et effectue les calculs sans eux. C'est ce qui s'est passé lorsque vous avez exécuté votre requête - les NULL ont été omis. Dans certains cas, vous voudrez remplacer les NULL par 0 (parce que la logique commerciale l'impose) ; c'est ce qu'a fait votre collègue, qui a produit des chiffres légèrement différents. Voici la requête de votre collègue : SELECT meal_category, avg(CASE WHEN no_of_views_lst_mth is null THEN 0 ELSE no_of_views_lst_mth END) AS average FROM recipes GROUP BY meal_category; Remarquez comment les moyennes de ces deux requêtes traitent les NULLs différemment. Par exemple, la catégorie "desserts" contient des NULLs. Ainsi, la première requête omet ces lignes et ne les compte pas dans le nombre total de lignes ; cela donne la valeur 144349.72. La deuxième requête remplace tous les NULLs par des zéros et compte ces lignes dans la moyenne, ce qui donne une valeur plus petite de 129914.75. Je dirais que les deux requêtes pourraient être valables, selon la façon dont vous voulez calculer les moyennes. 7. Utilisation de COUNT(*) avec GROUP BY et LEFT JOIN L'utilisation de GROUP BY avec une instruction LEFT JOIN peut être assez déroutante - surtout avec COUNT(). Voyons comment COUNT(*) et COUNT(expression) fonctionnent dans un LEFT JOIN. Supposons qu'une personne du service marketing dispose du tableau suivant, recipes_campaigns. Il contient des informations sur le nombre de campagnes menées sur chaque catégorie de repas pendant le mois en cours : meal_categorycampaigns Bread and pastry2 Cold appetizer1 desserts3 drinks0 Main dishes3 Salads1 Side dishes2 Soups3 Warm appetizer0 brunch1 sandwiches0 recettes_campagne En plus des données de recipes_campaignsle responsable marketing souhaite également connaître le nombre de recettes pour chaque catégorie de repas. Pour cela, nous aurons besoin des informations de la table recipes tableau. Nous allons donc joindre ces deux tableaux et calculer le nombre de recettes à l'aide de COUNT(*), comme suit : SELECT a.meal_category, count(*), FROM recipes_campaigns a LEFT JOIN recipes b ON a.meal_category=b.meal_category GROUP BY a.meal_category; Voici le résultat : meal_categorycount(*) Bread and pastry7 brunch1 Cold appetizer6 desserts20 drinks7 Main dishes20 Salads8 sandwiches1 Side dishes12 Soups17 Warm appetizer3 Ce n'est pas ce que nous attendions. Le tableau recipe ne contient aucune recette dans la catégorie "brunch", alors pourquoi avons-nous obtenu ce 1 dans le résultat ? Cela se produit parce que COUNT() est appliqué au résultat de LEFT JOIN! Lorsque vous consultez LEFT JOIN deux tableaux, la catégorie 'brunch' sera présente dans le résultat, même si aucune recette ou catégorie correspondante ne figure dans le tableau recipe table. Comment pouvons-nous résoudre ce problème ? Si nous utilisons COUNT(expression) au lieu de COUNT(*), nous obtiendrons le résultat souhaité : SELECT a.meal_category, count(author_id), FROM recipes_campaigns a LEFT JOIN recipes b ON a.meal_category=b.meal_category GROUP BY a.meal_category; Une fois que vous aurez exécuté ceci, vous obtiendrez : meal_categorycount(author_id) Bread and pastry7 brunch0 Cold appetizer6 desserts20 drinks7 Main dishes20 Salads8 sandwiches0 Side dishes12 Soups17 Warm appetizer3 Ici, COUNT(author_id) compte seulement les valeurs non NULL dans author_id après que LEFT JOIN soit exécuté. Il n'y a pas de valeur author_id pour la catégorie 'brunch' ; en d'autres termes, elle est NULL et le résultat pour cette catégorie est 0. Vous pouvez résoudre les erreurs GROUP BY ! À travers plusieurs exemples, nous avons exploré GROUP BY et les erreurs les plus courantes que les débutants commettent souvent. J'espère que vous avez maintenant une meilleure idée du fonctionnement de GROUP BY et de ce qui cause ces erreurs bizarres ou ces résultats confus. GROUP BY est vraiment très important dans la création de rapports. Si vous souhaitez apprendre à construire de bons rapports, je vous recommande notre Création de rapports basiques en SQL cours. Il comporte de nombreux exercices interactifs qui vous permettent d'acquérir de l'expérience dans la rédaction de requêtes GROUP BY pour les rapports. Et plus d'expérience réduit certainement la possibilité d'erreurs ! Tags: sql apprendre sql group by