Retour à la liste des articles Articles
9 minutes de lecture

Comment utiliser CASE WHEN dans GROUP BY

Découvrez comment combiner les instructions SQL CASE WHEN et GROUP BY pour créer des catégories personnalisées dans vos requêtes SQL.

Les données brutes, de par leur nature même, ne sont pas toujours lisibles par l'homme. Souvent, les données que vous interrogez sont dans leur forme la plus brute. C'est le cas, par exemple, des codes des différents départements de l'entreprise ou des UGS qui représentent des produits spécifiques. À l'œil nu, ces codes ne signifient rien, et les intégrer dans un rapport n'est donc pas utile pour la personne qui les lit.

Il existe également des situations où les données brutes doivent être regroupées à des niveaux plus élevés afin de les rendre plus faciles à assimiler. Par exemple, une liste de 50 produits peut être ramenée à 5 catégories de produits, ce qui rend le rapport beaucoup plus facile à lire et à comprendre.

Dans ce type de situation, nous pouvons utiliser les déclarations CASE WHEN et GROUP BY pour formater les données et ajouter des métadonnées à notre ensemble de données original. Nous étudierons le fonctionnement de ces déclarations tout au long de cet article. Pour un examen plus approfondi de ces concepts, nous vous invitons à consulter notre site Web Création de rapports basiques en SQL qui vous expliquera toutes les nuances de CASE et GROUP BY. Il s'agit d'une excellente suite à cet article.

Tout d'abord, examinons la déclaration CASE WHEN à l'aide de quelques exemples. Si vous souhaitez obtenir plus d'informations sur l'instruction CASE, lisez notre article Comment utiliser CASE en SQL.

Bref aperçu de CASE WHEN

Vous pouvez considérer l'instruction CASE WHEN comme la logique if..then de votre requête. Elle évalue les conditions et si la condition est vraie, elle renvoie un résultat spécifique défini.

Les instructions CASE en SQL comportent trois éléments importants : CASE WHEN, THEN, et END. Chacun de ces éléments est nécessaire, faute de quoi votre requête renverra une erreur.

Vous commencez l'instruction par CASE WHEN pour définir votre condition logique. Ensuite, vous utilisez THEN pour définir la valeur si cette condition est vraie. Après la dernière instruction THEN, vous utilisez END pour fermer la clause.

La clause ELSE est une autre clause facultative. Si toutes les conditions logiques de l'instruction CASE WHEN échouent, vous pouvez utiliser ELSE pour attribuer une valeur à ces données. Il s'agit en fait d'une clause "fourre-tout". Si vos données échouent à toutes les conditions et que vous n'utilisez pas ELSE, les données renverront une valeur NULL.

Voici à quoi ressemble la syntaxe de CASE WHEN:

CASE WHEN product = ‘Shirt’ THEN ‘Clothing’ 
     WHEN product = ‘Hat’ THEN ‘Accessories’ 
     ELSE ‘Other’
END 

Si le produit est une chemise, CASE WHEN l'affecte à la catégorie Clothing. Si le produit est un chapeau, CASE WHEN l'affecte à la catégorie Accessories. Dans le cas contraire, CASE WHEN attribue le produit à la catégorie Other.

Pour illustrer davantage, considérons les données suivantes trouvées dans le tableau cities dans le tableau :

citystateprice_to_income_ratiomortgage_as_pct_of_incomehomeowner_pctpopulation
Santa BarbaraCA13.3103.753%88,000
BrooklynNY11.289.930%2,533,862
QueensNY11.191.345%2,271,000
New YorkNY10.485.924%8,468,000
OaklandCA9.477.541%433,800
SunnyvaleCA9.376.545%152,300
San DiegoCA8.266.354%1,382,000
San FranciscoCA9.273.238%815,200
Long BeachCA8.569.641%456,000
BuffaloNY6.55343%276,800

Si nous voulions classer la population de chaque ville en trois catégories (faible, moyenne ou élevée), nous lancerions une requête :

SELECT 
  city,
  population, 
  CASE 
   WHEN population < 500000 THEN ‘Low’ 
   WHEN population >= 500000 and population < 1500000 THEN ‘Medium’ 
   WHEN population >= 1500000 THEN ‘High’ 
  END as population_level
FROM cities

Dans cette requête, nous ajoutons une logique conditionnelle basée sur le champ population. Si la population est inférieure à 500 000 habitants, nous attribuons la valeur Low. Si la population est comprise entre 500 000 et 1 500 000 habitants, nous attribuons la valeur Medium. Enfin, si la population est supérieure à 1 500 000 habitants, nous attribuons la valeur High. La colonne est alors aliasée population_level. Le résultat obtenu ressemble à ceci :

citypopulationpopulation_level
Santa Barbara88,000Low
Brooklyn2,533,862High
Queens2,271,000High
New York8,468,000High
Oakland433,800Low
Sunnyvale152,300Low
San Diego1,382,000Medium
San Francisco815,200Medium
Long Beach456,000Low
Buffalo276,800Low

Et si nous voulions connaître la population moyenne pour chaque site population_level? Dans ce scénario, nous pouvons y parvenir en incluant une clause d'agrégation et GROUP BY. Nous en ferons la démonstration dans la section suivante.

Utilisation de CASE WHEN avec GROUP BY

Exemple 1 : agrégations de catégories personnalisées

Maintenant que nous disposons de la catégorie personnalisée population_level, nous pouvons calculer différentes mesures pour cette catégorie. Dans cet exemple, nous allons calculer la population moyenne pour chaque site population_level.

Nous utiliserons la même déclaration CASE WHEN que ci-dessus, ajouterons une agrégation pour la moyenne, puis GROUP BY en utilisant la même syntaxe.

Exécutons cette requête :

SELECT  
  CASE 
   WHEN population < 500000 THEN ‘Low’ 
   WHEN population >= 500000 and population < 1500000 THEN ‘Medium’ 
   WHEN population >= 1500000 THEN ‘High’ 
  END as population_level, 
  AVG(population) as average_population
FROM cities
GROUP BY
  CASE 
   WHEN population < 500000 THEN ‘Low’ 
   WHEN population >= 500000 and population < 1500000 THEN ‘Medium’ 
   WHEN population >= 1500000 THEN ‘High’ 
  END

Nous avons utilisé la même catégorie de population que dans la requête ci-dessus. Nous avons ajouté un calcul pour la population moyenne. Notez que nous avons répété la même expression CASE WHEN dans SELECT et dans GROUP BY.

Lorsque cette requête est exécutée, les données sont d'abord évaluées dans les conditions logiques de l'instruction CASE WHEN et une valeur leur est attribuée pour population_level. La moyenne est ensuite calculée pour chacun de ces niveaux à l'aide de l'expression GROUP BY.

Les résultats obtenus ressemblent à ceci :

population_levelaverage_population
Low281,380
Medium1,098,600
High4,424,287

Si vous avez besoin d'un rappel sur la clause GROUP BY, lisez cet article sur le GROUP BY en SQL.

Exemple 2 : CASE WHEN avec ELSE dans GROUP BY

Une autre façon d'écrire cette requête serait d'utiliser la clause ELSE. Vous définiriez les deux premiers niveaux de population et utiliseriez ensuite ELSE pour regrouper toutes les autres villes dans high. Cette requête ressemblerait à la suivante :

SELECT  
  CASE 
    WHEN population < 500000 THEN ‘Low’ 
    WHEN population >= 500000 and population < 1500000 THEN ‘Medium’ 
    ELSE ‘High’ 
  END as population_level, 
  AVG(population) as average_population
FROM cities
GROUP BY 
  CASE 
   WHEN population < 500000 THEN ‘Low’ 
   WHEN population >= 500000 and population < 1500000 THEN ‘Medium’ 
   WHEN population >= 1500000 THEN ‘High’ 
  END

Nous n'avons pas nécessairement besoin de définir la logique pour les villes à forte population ; si la ville échoue aux deux premiers énoncés logiques, c'est que sa population est supérieure à 1 500 000 habitants.

Exemple 3 : déclarations CASE WHEN à l'intérieur d'agrégations

Nous pouvons également placer les instructions CASE WHEN à l'intérieur des fonctions d'agrégation pour compter le nombre d'enregistrements qui remplissent certaines conditions. En reprenant le même exemple, examinons comment nous pourrions compter le nombre de villes sur chaque site population_level.

Pour cet exemple, nous aurons besoin de trois instructions CASE WHEN distinctes, une pour chaque condition définie dans notre instruction CASE WHEN: low, medium, et high.

SELECT  
  SUM(CASE 
      WHEN population < 500000 
      THEN 1 
      ELSE 0 END) as low_pop_ct,
  SUM(CASE 
      WHEN population >= 500000 and population < 1500000 
      THEN 1   
      ELSE 0 END) as medium_pop_ct, 
  SUM(CASE 
      WHEN population >= 1500000 
      THEN 1 
      ELSE 0 END) as high_pop_ct
FROM cities

Voyons cela ligne par ligne. Dans la première ligne, nous évaluons si la ville a une population inférieure à 500 000 habitants. Si c'est le cas, nous lui attribuons la valeur 1. Dans le cas contraire, la clause ELSE lui attribue la valeur 0. Nous avons ensuite enveloppé l'ensemble de l'instruction CASE WHEN dans une clause SUM(), ce qui a pour effet de compter les lignes des villes à faible population.

Nous répétons ensuite ce schéma dans les deux lignes suivantes, en utilisant les mêmes conditions logiques que précédemment pour attribuer la valeur de medium et high aux villes en fonction de leur population.

Cette requête renvoie l'information suivante :

low_pop_ctmedium_pop_cthigh_pop_ct
523

Nous disposons à présent d'un décompte du nombre de villes entrant dans chaque catégorie.

Vous souhaitez en savoir plus ? Consultez la rubrique Comment utiliser CASE WHEN avec SUM() pour plus d'exemples.

Exemple 4 : CASE WHEN dans GROUP BY

Voyons quelques autres exemples d'utilisation de CASE WHEN dans GROUP BY. Les données ci-dessous sont contenues dans le tableau products.

skudescriptionpricestatus
978568952cowl neck sweater59in stock
978548759embroidered v neck blouse49in stock
978125698notched collar button down blazer79in stock
979156258oversized stripe shirt29sale
979145875polka dot maxi dress109back ordered
978457852rib knit t shirt19sale
978333562cropped denim jacket99back ordered
978142154sleeveless midi dress89in stock
979415858utility jumpsuit59sale
978112546scoop neck sweater49in stock

Ajoutons à ces données une nouvelle colonne contenant un product_category. Cela nous permettra de regrouper les produits individuels dans une catégorie de niveau supérieur afin de pouvoir calculer les agrégations.

Afin d'ajouter une colonne pour product_category, nous allons exécuter :

SELECT *, 
  CASE WHEN description LIKE '%sweater%' 
         OR description LIKE '%blazer%' 
         OR description LIKE '%jacket%' THEN  'Outerwear'
       WHEN description LIKE '%dress%' 
         OR description LIKE '%jumpsuit%' THEN  'Dresses' 
       WHEN description LIKE '%shirt%' 
         OR description LIKE '%blouse%' THEN 'Tops'
  END as product_category 
FROM products

Nous utilisons l'opérateur LIKE pour rechercher dans le champ de description chaque chaîne entre guillemets. L'opérateur % signifie simplement qu'il peut y avoir quelque chose avant ou après. Nous recherchons en fait dans chaque description l'un des mots clés énumérés dans les déclarations WHEN. Les résultats sont présentés ci-dessous :

skudescriptionpricestatusproduct_category
978568952cowl neck sweater59in stockOuterwear
978548759embroidered v neck blouse49in stockTops
978125698notched collar button down blazer79in stockOuterwear
979156258oversized stripe shirt29saleTops
979145875polka dot maxi dress109back orderedDresses
978457852rib knit t shirt19saleTops
978333562cropped denim jacket99back orderedOuterwear
978142154sleeveless midi dress89in stockDresses
979415858utility jumpsuit59saleDresses
978112546scoop neck sweater49in stockOuterwear

Maintenant que nous avons notre nouveau champ product_category, nous pouvons compter le nombre de produits dans chaque catégorie en utilisant GROUP BY:

SELECT
  CASE WHEN description LIKE '%sweater%' 
         OR description LIKE '%blazer%' 
         OR description LIKE '%jacket%' THEN  'Outerwear'
       WHEN description LIKE '%dress%' 
         OR description LIKE '%jumpsuit%' THEN  'Dresses' 
       WHEN description LIKE '%shirt%' 
         OR description LIKE '%blouse%' THEN 'Tops'
   END as product_category, 
   COUNT(DISTINCT description) as number_of_products
FROM products 
GROUP BY 
  CASE WHEN description LIKE '%sweater%' 
         OR description LIKE '%blazer%' 
         OR description LIKE '%jacket%' THEN  'Outerwear'
       WHEN description LIKE '%dress%' 
         OR description LIKE '%jumpsuit%' THEN  'Dresses' 
       WHEN description LIKE '%shirt%' 
         OR description LIKE '%blouse%' THEN 'Tops'
  END 
product_categorynumber_of_products
Outerwear4
Tops3
Dresses3

Dans cet exemple, nous avons utilisé CASE WHEN dans la clause SELECT, mais cela n'est pas toujours nécessaire. Vous pouvez également exécuter la requête ci-dessus sans cette clause :

SELECT COUNT(DISTINCT description) as number_of_products
FROM products 
GROUP BY 
  CASE WHEN description LIKE '%sweater%' 
         OR description LIKE '%blazer%' 
         OR description LIKE '%jacket%' THEN  'Outerwear'
       WHEN description LIKE '%dress%' 
         OR description LIKE '%jumpsuit%' THEN  'Dresses' 
       WHEN description LIKE '%shirt%' 
         OR description LIKE '%blouse%' THEN 'Tops'
  END 
number_of_products
4
3
3

Que se passe-t-il ensuite avec CASE WHEN et GROUP BY ?

Comme vous pouvez le constater, il existe de nombreux scénarios dans lesquels la combinaison des instructions CASE WHEN et GROUP BY est extrêmement utile. Elles vous permettent d'ajouter une logique métier à vos données, puis de calculer des mesures basées sur les champs de données nouvellement définis. Vous pouvez utiliser les instructions CASE WHEN aussi bien en dehors qu'à l'intérieur des agrégations ; elles suivent la même syntaxe. Commencez votre déclaration par CASE WHEN pour définir votre logique conditionnelle, puis attribuez des valeurs à l'aide des déclarations THEN/ELSE. Enfin, terminez par END.

N'oubliez pas de consulter notre cours sur Création de rapports basiques en SQL. Vous apprendrez toutes les nuances de CASE et GROUP BY et vous aurez des problèmes pratiques réels à résoudre !