Clause WITH de MySQL (CTE) expliquée : syntaxe, exemples, requêtes récursives et conseils de performance

目次

1. Introduction

MySQL est un système de gestion de bases de données largement utilisé par les développeurs et les administrateurs de bases de données, offrant une fonctionnalité SQL puissante et flexible. Parmi ses fonctionnalités, la clause WITH (Expression de Table Commune : CTE), introduite dans MySQL 8.0, est un outil puissant qui rend les requêtes SQL plus lisibles et améliore la maintenabilité.

Cet article explique la clause WITH des bases aux usages avancés, à destination des débutants et des utilisateurs intermédiaires. En particulier, nous couvrons des sujets pratiques tels que le remplacement des sous‑requêtes et la mise en œuvre de requêtes récursives.

Pour ceux qui apprennent le SQL ou qui peinent à optimiser leurs requêtes au quotidien, cet article vise à fournir des solutions concrètes. Suivez le contenu ci‑dessous pour comprendre les fondamentaux de la clause WITH et l’appliquer dans des scénarios réels.

2. Bases de la clause WITH (Expressions de Table Communes)

Qu’est‑ce que la clause WITH ?

La clause WITH est une syntaxe utilisée pour définir un ensemble de résultats temporaire (Expression de Table Commune, CTE) au sein d’une requête SQL et le référencer dans les requêtes suivantes. Supportée depuis MySQL 8.0, elle permet de réécrire des sous‑requêtes complexes de façon plus claire et plus concise.

Par exemple, lorsqu’on écrit des sous‑requêtes directement, la lisibilité peut diminuer et la requête globale peut devenir très longue. En utilisant la clause WITH, vous pouvez diviser les requêtes en blocs logiques, ce qui les rend plus faciles à comprendre.

Syntaxe de base de la clause WITH

Voici la syntaxe de base de la clause WITH :

WITH table_name AS (
  SELECT column1, column2
  FROM original_table
  WHERE condition
)
SELECT column1, column2
FROM table_name;

Dans cette syntaxe, une table virtuelle (Expression de Table Commune) est définie après WITH, puis utilisée dans la requête principale. Cela rend les sous‑requêtes utilisées de façon répétée plus simples à exprimer de manière concise.

Différences avec les sous‑requêtes et les vues

La clause WITH crée un ensemble de résultats disponible temporairement et diffère des sous‑requêtes et des vues à plusieurs égards.

FeatureWITH ClauseSubqueryView
ScopeValid only within the queryUsable only where definedReusable across the entire database
PersistenceTemporaryTemporaryPermanent
PurposeSimplifies complex queriesTemporary data extractionFrequently reused data extraction

La clause WITH est plus lisible que les sous‑requêtes et est idéale lorsque vous n’avez pas besoin de créer un objet permanent comme une vue.

Avantages de l’utilisation de la clause WITH

  1. Lisibilité améliorée : même lorsqu’il existe plusieurs sous‑requêtes, les organiser avec la clause WITH clarifie la structure.
  2. Réutilisabilité accrue : en définissant un ensemble de résultats temporaire, il peut être référencé plusieurs fois dans la même requête.
  3. Maintenabilité renforcée : comme les requêtes peuvent être divisées logiquement, les modifications et extensions deviennent plus simples.

3. Utilisation de base de la clause WITH de MySQL

Remplacement des sous‑requêtes

La clause WITH est un outil puissant pour simplifier les sous‑requêtes complexes. Intégrer directement des sous‑requêtes peut rendre la requête entière compliquée et difficile à lire, mais l’utilisation de la clause WITH améliore la lisibilité.

Voici un exemple de base de remplacement d’une sous‑requête à l’aide de la clause WITH.

Utilisation d’une sous‑requête :

SELECT AVG(sales.total) AS average_sales
FROM (
  SELECT SUM(amount) AS total
  FROM orders
  GROUP BY customer_id
) AS sales;

Utilisation de la clause WITH :

WITH sales AS (
  SELECT SUM(amount) AS total
  FROM orders
  GROUP BY customer_id
)
SELECT AVG(sales.total) AS average_sales
FROM sales;

Dans cet exemple, un ensemble de résultats temporaire nommé sales est défini avec la clause WITH puis référencé dans la requête principale. Cela rend la requête entière plus facile à comprendre et mieux organisée.

Définition de plusieurs Expressions de Table Communes (CTE)

La clause WITH permet de définir plusieurs CTE. Cela rend possible la modularisation supplémentaire de requêtes complexes.

Exemple :

WITH 
  sales_per_customer AS (
    SELECT customer_id, SUM(amount) AS total_sales
    FROM orders
    GROUP BY customer_id
  ),
  high_value_customers AS (
    SELECT customer_id
    FROM sales_per_customer
    WHERE total_sales > 10000
  )
SELECT customer_id
FROM high_value_customers;

Dans cet exemple, sales_per_customer calcule le total des ventes par client, et à partir de ce résultat, high_value_customers extrait les clients avec des montants d’achat élevés. En utilisant plusieurs CTEs séquentiellement, les requêtes peuvent être construites étape par étape.

Utilisation de CTE imbriqués

En utilisant des CTE imbriqués, vous pouvez effectuer des opérations de données plus complexes.

Example:

WITH 
  sales_data AS (
    SELECT product_id, SUM(amount) AS total_sales
    FROM orders
    GROUP BY product_id
  ),
  ranked_sales AS (
    SELECT product_id, total_sales,
           RANK() OVER (ORDER BY total_sales DESC) AS rank
    FROM sales_data
  )
SELECT product_id, total_sales
FROM ranked_sales
WHERE rank <= 5;

Dans cette requête, sales_data agrège les ventes par produit, et ranked_sales attribue des classements basés sur le volume des ventes. Enfin, les cinq meilleurs produits sont extraits.

Points clés pour une utilisation pratique

  1. Penser en étapes logiques Construisez les CTE étape par étape pour améliorer la lisibilité et faciliter le débogage.
  2. Stocker les résultats de calcul intermédiaires Regroupez les résultats de calcul ou les conditions de filtrage utilisées plusieurs fois dans des CTE afin de réduire la duplication du code.
  3. Faire attention aux grands ensembles de données Puisque les CTE génèrent des ensembles de résultats temporaires, considérez l’impact sur les performances lorsque vous travaillez avec de grandes quantités de données.

4. Exemples pratiques de clauses WITH récursives

Qu’est-ce qu’une clause WITH récursive ?

Une clause WITH récursive (CTE récursif) est une méthode qui utilise une expression de table commune pour exécuter de façon répétée une requête auto‑référencée, vous permettant de traiter des données hiérarchiques et des calculs itératifs. Les CTE récursifs sont pris en charge dans MySQL 8.0 et versions ultérieures et sont particulièrement utiles lors du travail avec des relations parent‑enfant et des structures hiérarchiques.

Syntaxe de base d’un CTE récursif

Pour définir un CTE récursif, utilisez le mot‑clé WITH RECURSIVE. La syntaxe de base est la suivante :

WITH RECURSIVE recursive_table_name AS (
  initial_query -- starting point of the recursion
  UNION ALL
  recursive_query -- query called recursively
)
SELECT * FROM recursive_table_name;
  • Requête initiale : Récupère le premier jeu de données pour le processus récursif.
  • Requête récursive : Génère de nouvelles lignes basées sur la requête initiale ou les résultats de l’itération précédente.
  • UNION ALL : Combine les résultats de la requête initiale et de la requête récursive.

Exemple : Traitement de données hiérarchiques

Les CTE récursifs sont souvent utilisés pour développer des données avec une structure hiérarchique (par ex., des arbres d’organisation ou des arbres de catégories).

Exemple : Expansion d’une hiérarchie de gestion des employés

Considérez la table employees suivante :

employee_idnamemanager_id
1AliceNULL
2Bob1
3Charlie1
4David2

En utilisant ces données, nous pouvons créer une requête qui récupère la hiérarchie complète à partir d’un employé donné.

WITH RECURSIVE employee_hierarchy AS (
  -- Initial query: get top-level employees
  SELECT employee_id, name, manager_id, 1 AS level
  FROM employees
  WHERE manager_id IS NULL

  UNION ALL

  -- Recursive query: get direct reports
  SELECT e.employee_id, e.name, e.manager_id, eh.level + 1
  FROM employees e
  INNER JOIN employee_hierarchy eh
  ON e.manager_id = eh.employee_id
)
SELECT * FROM employee_hierarchy;

Résultat :

employee_idnamemanager_idlevel
1AliceNULL1
2Bob12
3Charlie12
4David23

Dans cette requête, les subordonnés sont recherchés de manière récursive en fonction de manager_id, ce qui développe la hiérarchie complète.

Limitations et précautions pour les CTE récursifs

  1. Une condition de fin est requise Si la requête récursive ne rencontre pas de condition de fin, une boucle infinie peut se produire. Incluez toujours des conditions appropriées pour éviter une récursion infinie.
  2. Impact sur les performances Les CTE récursifs peuvent impliquer de nombreux calculs sur de grands ensembles de données, ce qui peut augmenter le temps d’exécution. Utilisez des clauses LIMIT et des conditions de filtrage pour améliorer l’efficacité.
  3. Limites de profondeur de récursion MySQL impose une limite à la profondeur de récursion, il faut donc faire attention lors de traitements récursifs très profonds. Cette limite peut être configurée avec le paramètre max_recursive_iterations.

Scénarios où les CTE récursifs sont utiles

  • Parcourir les structures de dossiers : Rechercher récursivement les dossiers et sous-dossiers.
  • Construire des organigrammes : Visualiser les hiérarchies des managers aux subordonnés.
  • Afficher les arbres de catégories : Récupérer les catégories de produits hiérarchiques ou les structures de tags.

Les CTE récursifs sont un moyen puissant d’écrire des requêtes SQL concises pour ces scénarios tout en améliorant la lisibilité.

5. Notes et considérations lors de l’utilisation de la clause WITH

Impact sur les performances et optimisation

  1. Recalcul du CTE En général, un CTE défini avec la clause WITH est recalculé chaque fois qu’il est référencé. Par conséquent, utiliser le même CTE plusieurs fois peut augmenter le temps d’exécution de la requête. Exemple :
    WITH sales AS (
      SELECT product_id, SUM(amount) AS total_sales
      FROM orders
      GROUP BY product_id
    )
    SELECT * FROM sales WHERE total_sales > 1000;
    SELECT COUNT(*) FROM sales;
    

Dans le cas ci‑dessus, sales est référencé deux fois, il est donc calculé deux fois. Pour éviter cela, lorsque vous devez référencer les résultats plusieurs fois, stocker le résultat dans une table temporaire peut être efficace.

Solution :

CREATE TEMPORARY TABLE temp_sales AS
SELECT product_id, SUM(amount) AS total_sales
FROM orders
GROUP BY product_id;

SELECT * FROM temp_sales WHERE total_sales > 1000;
SELECT COUNT(*) FROM temp_sales;
  1. Diviser les CTE complexes Si la clause WITH devient trop profondément imbriquée, la requête globale peut devenir complexe et plus difficile à déboguer. Il est important de scinder la logique de manière appropriée afin que le traitement au sein d’un seul CTE ne devienne pas excessivement compliqué.

Utiliser les clauses WITH sur de grands ensembles de données

La clause WITH génère des ensembles de données temporaires pendant l’exécution. Lors du traitement de grandes quantités de données, cela peut imposer une charge sur la mémoire ou le stockage.

Contremesures :

  • Filtrer les données avec des clauses WHERE Réduire le calcul en filtrant les données inutiles à l’intérieur du CTE.
    WITH filtered_orders AS (
      SELECT *
      FROM orders
      WHERE order_date > '2023-01-01'
    )
    SELECT customer_id, SUM(amount)
    FROM filtered_orders
    GROUP BY customer_id;
    
  • Utiliser des clauses LIMIT Si l’ensemble de données est volumineux, utilisez LIMIT pour extraire uniquement les données dont vous avez besoin.

Compatibilité des versions MySQL

La clause WITH dans MySQL est prise en charge à partir de MySQL 8.0 et versions ultérieures. Étant donné que les versions antérieures ne supportent pas la clause WITH, vous devez envisager des alternatives.

Alternatives :

  • Utiliser des sous‑requêtes Utilisez des sous‑requêtes directement au lieu de la clause WITH.
    SELECT AVG(total_sales)
    FROM (
      SELECT customer_id, SUM(amount) AS total_sales
      FROM orders
      GROUP BY customer_id
    ) AS sales;
    
  • Créer des vues Si vous avez besoin de requêtes réutilisables, l’utilisation d’une vue peut être efficace.
    CREATE VIEW sales_view AS
    SELECT customer_id, SUM(amount) AS total_sales
    FROM orders
    GROUP BY customer_id;
    
    SELECT AVG(total_sales) FROM sales_view;
    

Comment utiliser correctement la clause WITH

  1. Prioriser la lisibilité Le but de la clause WITH est d’organiser les requêtes et d’améliorer la lisibilité. En abuser peut rendre les requêtes plus complexes, il faut donc l’utiliser uniquement lorsque c’est nécessaire.
  2. Vérifier les performances Vérifiez le plan d’exécution (la commande EXPLAIN) et réfléchissez à la manière d’optimiser les performances.
    EXPLAIN
    WITH sales AS (
      SELECT product_id, SUM(amount) AS total_sales
      FROM orders
      GROUP BY product_id
    )
    SELECT * FROM sales WHERE total_sales > 1000;
    

6. Cas d’utilisation pratiques dans des scénarios réels

Agrégation des données de ventes

Voici un exemple d’agrégation des données de ventes par mois, puis d’utilisation de ce résultat pour calculer la moyenne mensuelle des ventes.

Exemple : Agrégation des ventes mensuelles et calcul de la moyenne

WITH monthly_sales AS (
  SELECT 
    DATE_FORMAT(order_date, '%Y-%m') AS sales_month,
    SUM(amount) AS total_sales
  FROM orders
  GROUP BY sales_month
)
SELECT 
  sales_month, 
  total_sales,
  AVG(total_sales) OVER () AS average_sales
FROM monthly_sales;

Dans cette requête, monthly_sales calcule les ventes par mois, et à partir de ce résultat, la moyenne globale des ventes est calculée. Cela permet d’organiser les données clairement et simplifie l’analyse.

Filtrage des données selon des conditions spécifiques

En séparant la logique de filtrage complexe dans une clause WITH, la lisibilité peut être améliorée.

Exemple : Création d’une liste de clients à forte dépense

WITH customer_totals AS (
  SELECT 
    customer_id, 
    SUM(amount) AS total_spent
  FROM orders
  GROUP BY customer_id
)
SELECT 
  customer_id, 
  total_spent
FROM customer_totals
WHERE total_spent > 100000;

Dans cette requête, customer_totals calcule le montant total des achats par client, et les clients répondant à la condition spécifiée sont extraits.

Analyse des données hiérarchiques

Lors de l’analyse de données hiérarchiques telles que les structures organisationnelles ou les catégories, les clauses WITH récursives sont extrêmement utiles.

Exemple : Récupération d’une liste de subordonnés directs et indirects

WITH RECURSIVE employee_hierarchy AS (
  SELECT 
    employee_id, 
    name, 
    manager_id, 
    1 AS level
  FROM employees
  WHERE manager_id IS NULL

  UNION ALL

  SELECT 
    e.employee_id, 
    e.name, 
    e.manager_id, 
    eh.level + 1
  FROM employees e
  INNER JOIN employee_hierarchy eh
  ON e.manager_id = eh.employee_id
)
SELECT 
  employee_id, 
  name, 
  manager_id, 
  level
FROM employee_hierarchy
ORDER BY level, manager_id;

Cette requête construit des données hiérarchiques dans employee_hierarchy et récupère les employés regroupés par niveau. Elle permet la génération dynamique d’informations similaires à un organigramme.

Analyse avancée en utilisant plusieurs CTE

En exploitant plusieurs clauses WITH, les données peuvent être traitées étape par étape, rendant l’analyse complexe plus simple.

Exemple : Extraction des produits les plus vendus par catégorie

WITH category_sales AS (
  SELECT 
    category_id, 
    product_id, 
    SUM(amount) AS total_sales
  FROM orders
  GROUP BY category_id, product_id
),
ranked_sales AS (
  SELECT 
    category_id, 
    product_id, 
    total_sales,
    RANK() OVER (PARTITION BY category_id ORDER BY total_sales DESC) AS rank
  FROM category_sales
)
SELECT 
  category_id, 
  product_id, 
  total_sales
FROM ranked_sales
WHERE rank <= 3;

Dans cette requête, les ventes par catégorie sont calculées, et les trois meilleurs produits de chaque catégorie sont extraits. Cette approche est efficace pour affiner les données en fonction de conditions spécifiques.

Points clés pour une application pratique

  1. Concevoir les requêtes en étapes logiques Utilisez la clause WITH pour diviser les requêtes et traiter les données étape par étape tout en conservant la lisibilité.

  2. Extraire uniquement les données nécessaires Utilisez les clauses WHERE et LIMIT pour éviter de traiter des données superflues et concevoir des requêtes efficaces.

  3. Applications métier flexibles La clause WITH peut être appliquée de manière flexible à l’analyse des ventes, à la segmentation des clients, à la gestion des stocks, et plus encore.

7. FAQ (Foire aux questions)

Q1 : Quand faut‑il utiliser la clause WITH ?

R1 :
La clause WITH est particulièrement efficace dans les scénarios suivants :

  • Lorsque vous souhaitez simplifier des sous‑requêtes complexes.
  • Lorsque vous avez besoin de réutiliser le même jeu de données plusieurs fois dans une requête.
  • Lorsque vous voulez diviser logiquement une requête afin d’améliorer la lisibilité.

Par exemple, dans les requêtes qui utilisent les mêmes résultats d’agrégation plusieurs fois, la clause WITH permet une organisation plus efficace.

Q2 : Quand une CTE récursive est‑elle utile ?

R2 :
Les CTE récursives sont utiles lors du traitement de structures hiérarchiques ou de calculs itératifs. Plus précisément :

  • Traitement de données hiérarchiques (par ex., arbres organisationnels, structures de catégories).
  • Affichage de hiérarchies de dossiers ou de fichiers.
  • Calculs séquentiels de nombres ou de périodes (par ex., calcul de la suite de Fibonacci).

L’utilisation de CTE récursives facilite l’expansion et le traitement de données auto‑référencées.

Q3 : Les requêtes utilisant la clause WITH sont‑elles plus efficaces que les vues ?

A3 :
Cela dépend du cas d’utilisation.

  • Clause WITH : Crée un ensemble de résultats temporaire utilisé uniquement dans la requête. Convient aux données qui n’ont pas besoin d’être réutilisées fréquemment.
  • Vue : Stockée de façon permanente dans la base de données et réutilisable par d’autres requêtes. Convient aux requêtes qui sont utilisées de manière répétée.

Choisir la méthode appropriée en fonction du scénario est important.

Q4 : Quelles sont les causes de la dégradation des performances lors de l’utilisation de la clause WITH ?

A4 :
Les principales causes de dégradation des performances lors de l’utilisation de la clause WITH sont les suivantes :

  • Recalcul du CTE : Les résultats sont recalculés à chaque référence, augmentant le temps de traitement.
  • Gestion de grands ensembles de données : La génération de grands ensembles de données dans le CTE augmente l’utilisation de la mémoire et réduit les performances.
  • Absence d’indexation appropriée : Si les requêtes à l’intérieur du CTE n’utilisent pas d’index adéquats, les performances peuvent ralentir.

Contremesures :

  • Envisagez d’utiliser des tables temporaires ou des vues si la fréquence de réutilisation est élevée.
  • Utilisez les clauses WHERE et LIMIT pour restreindre correctement les données.

Q5 : Quelles alternatives existent pour les versions de MySQL qui ne prennent pas en charge la clause WITH ?

A5 :
Dans les versions antérieures à MySQL 8.0, la clause WITH n’est pas prise en charge, il faut donc utiliser les alternatives suivantes :

  • Utiliser des sous‑requêtes Utilisez directement des sous‑requêtes au lieu de la clause WITH.
    SELECT AVG(total_sales)
    FROM (
      SELECT customer_id, SUM(amount) AS total_sales
      FROM orders
      GROUP BY customer_id
    ) AS sales;
    
  • Utiliser des tables temporaires Stockez les ensembles de données réutilisables dans une table temporaire.
    CREATE TEMPORARY TABLE temp_sales AS
    SELECT customer_id, SUM(amount) AS total_sales
    FROM orders
    GROUP BY customer_id;
    
    SELECT AVG(total_sales) FROM temp_sales;
    

Q6 : Quelles sont les meilleures pratiques lors de l’utilisation de la clause WITH ?

A6 :
Gardez à l’esprit les meilleures pratiques suivantes :

  • Prioriser la simplicité : Ne forcez pas une logique complexe dans une seule clause WITH. Divisez‑la de manière appropriée.
  • Vérifier les performances : Consultez le plan d’exécution avec la commande EXPLAIN et optimisez si nécessaire.
  • Considérer la réutilisabilité : Si la fréquence de réutilisation est élevée, exploitez les vues ou les tables temporaires.

8. Conclusion

Cet article a couvert la clause WITH (Common Table Expression, CTE) introduite dans MySQL 8.0, des fondamentaux aux applications avancées. La clause WITH est une fonctionnalité extrêmement utile pour rendre les requêtes complexes concises et lisibles. Voici les points clés à retenir.

Principaux avantages de la clause WITH

  1. Lisibilité améliorée des requêtes Organise les sous‑requêtes complexes, améliorant la lisibilité et la maintenabilité du code SQL.
  2. Réutilisabilité des requêtes Traite efficacement les données lorsqu’on fait référence au même ensemble de données plusieurs fois.
  3. Permet les opérations de données récursives Les CTE récursifs simplifient le traitement des données hiérarchiques et les calculs itératifs.

Points d’utilisation pratiques

  • Utile pour l’analyse des ventes et des données clients, permettant une agrégation étape par étape.
  • Les CTE récursifs sont efficaces pour le traitement de données hiérarchiques (comme les organigrammes ou les structures de catégories).
  • Combiner les clauses WITH avec des vues ou des tables temporaires permet des opérations de base de données flexibles et efficaces.

Considérations importantes

  • La clause WITH est puissante, mais une utilisation inappropriée peut réduire les performances.
  • Évaluez la réutilisabilité et les performances au cas par cas, et choisissez entre vues ou tables temporaires selon le contexte.
  • Vérifiez toujours l’efficacité de la requête à l’aide du plan d’exécution (commande EXPLAIN).

Prochaines étapes

En utilisant la clause WITH, vous pouvez créer des requêtes SQL plus efficaces et plus maintenables. Essayez de l’appliquer à vos projets réels en suivant ces étapes :

  1. Commencez par des requêtes simples et entraînez‑vous à les structurer avec la clause WITH.
  2. Mettez‑vous au défi avec des CTE récursifs pour les données hiérarchiques et les scénarios complexes.
  3. Concentrez‑vous sur l’optimisation des performances pour améliorer davantage vos compétences SQL.

Ceci conclut l’article. Utilisez vos connaissances de la clause WITH de MySQL dans votre travail quotidien et vos études.