Découvrez comment la clause WITH en SQL structure vos requêtes et améliore la lisibilité. Exemples pratiques et conseils pour entretiens data.
Thomas Leroy
La clause WITH en SQL — aussi appelée CTE (Common Table Expression) — permet de nommer une sous-requête et de la réutiliser plusieurs fois dans une même instruction. En entretien data, savoir écrire une requête lisible avec WITH est souvent ce qui distingue un candidat junior d'un profil confirmé.
Beaucoup de développeurs écrivent des sous-requêtes imbriquées à 3 ou 4 niveaux de profondeur. Le résultat est techniquement correct, mais illisible. La clause WITH résout ce problème en découpant la logique en blocs nommés, comme des fonctions dans un langage de programmation.
Dans cet article, vous allez comprendre la syntaxe exacte de WITH, ses différences avec une sous-requête classique, ses cas d'usage en production, et les pièges à éviter en entretien technique.
📌 Ce qu'il faut retenir
WITH définit une ou plusieurs sous-requêtes temporaires réutilisables dans la requête principale
Une CTE améliore la lisibilité sans nécessairement changer les performances
Plusieurs CTEs peuvent être enchaînées avec une virgule dans le même bloc WITH
Certains SGBD (PostgreSQL, SQL Server) supportent les CTEs récursives via WITH RECURSIVE
La syntaxe de base de la clause WITH
La structure d'une clause WITH suit toujours le même schéma : on déclare le nom de la CTE, on indique les colonnes optionnellement, puis on écrit la sous-requête entre parenthèses.
WITH nom_cte AS (
SELECT colonne1, colonne2
FROM ma_table
WHEREcondition
)
SELECT*FROM nom_cte
WHERE autre_condition;
La CTE est éphémère : elle n'existe que le temps de l'exécution de la requête. Elle n'est pas stockée en base comme une vue. Une fois la requête terminée, elle disparaît.
On peut également déclarer plusieurs CTEs en les séparant par une virgule :
WITH
cte1 AS (SELECT ...),
cte2 AS (SELECT ... FROM cte1)
SELECT*FROM cte2;
Notez que cte2 peut référencer cte1 directement. L'ordre de déclaration est donc important.
WITH vs sous-requête imbriquée : quand choisir l'un ou l'autre ?
La question revient souvent en entretien. La réponse courte : WITH est presque toujours préférable dès que la logique dépasse 2 niveaux d'imbrication.
Critère
Sous-requête imbriquée
Clause WITH (CTE)
Lisibilité
Difficile dès 2 niveaux
Excellente, logique séparée en blocs
Réutilisabilité
Impossible, à répéter
La CTE est référencée autant de fois que nécessaire
Imaginons une table ventes avec les colonnes vendeur, categorie et montant. On veut afficher les 3 meilleurs vendeurs par catégorie.
WITH classement AS (
SELECT
vendeur,
categorie,
montant,
RANK() OVER (PARTITIONBY categorie ORDERBY montant DESC) AS rang
FROM ventes
)
SELECT vendeur, categorie, montant
FROM classement
WHERE rang <=3;
Sans WITH, cette requête aurait nécessité une sous-requête imbriquée dans le FROM. Avec WITH, la logique de classement est isolée et immédiatement compréhensible.
Filtrer après agrégation
WITH est très pratique pour appliquer un filtre après un GROUP BY, sans avoir à utiliser HAVING ou une sous-requête.
WITH chiffre_affaires AS (
SELECT
client_id,
SUM(montant) AS total
FROM commandes
GROUPBY client_id
)
SELECT client_id, total
FROM chiffre_affaires
WHERE total >10000ORDERBY total DESC;
Ce pattern — agréger dans une CTE, filtrer dans la requête principale — est l'un des plus fréquents en analyse de données.
💡 Bon à savoir
Pendant un entretien, si vous hésitez entre HAVING et une CTE, choisissez la CTE dès que vous avez besoin de référencer le résultat agrégé dans plusieurs parties de la requête. C'est une décision qui montre de la maturité technique.
Combiner plusieurs étapes analytiques
Un cas très courant en data analyse : calculer une métrique en plusieurs étapes (taux de conversion, panier moyen, rétention). Chaque étape devient une CTE.
WITH
commandes_par_client AS (
SELECT client_id, COUNT(*) AS nb_commandes
FROM commandes
GROUPBY client_id
),
clients_actifs AS (
SELECT client_id
FROM commandes_par_client
WHERE nb_commandes >=3
)
SELECT c.nom, c.email
FROM clients c
INNERJOIN clients_actifs ca ON c.id = ca.client_id;
Ce type de requête en cascade est exactement ce que les recruteurs cherchent à voir : une logique claire, structurée, et maintenable.
Les erreurs fréquentes avec WITH
Déclarer une CTE sans l'utiliser
Certains SGBD comme PostgreSQL retournent une erreur si une CTE est déclarée mais jamais référencée dans la requête principale. C'est une erreur qui arrive souvent lors du refactoring.
Oublier la virgule entre deux CTEs
La virgule entre deux CTEs est obligatoire. Oublier ce détail provoque une erreur de syntaxe immédiate. Le mot-clé WITH ne s'écrit qu'une seule fois, au début du bloc.
⚠️ Attention
Dans certains SGBD comme MySQL avant la version 8.0, la clause WITH n'est pas supportée. Vérifiez toujours la version du moteur utilisé en entretien ou en production avant d'utiliser les CTEs.
Confondre CTE et vue matérialisée
Une CTE n'est pas mise en cache entre plusieurs références dans la même requête sur tous les SGBD. Dans PostgreSQL, la CTE est matérialisée par défaut (jusqu'à la version 12), ce qui peut parfois dégrader les performances. À partir de PostgreSQL 12, le comportement par défaut est laissé à l'optimiseur, mais vous pouvez forcer avec WITH ... AS MATERIALIZED ou WITH ... AS NOT MATERIALIZED.
Pour les cas où vous avez besoin d'une vue persistante et optimisée, consultez notre guide sur les vues matérialisées SQL.
WITH et les fonctions fenêtre : une combinaison puissante
Les fonctions fenêtre comme RANK(), LAG() ou SUM() OVER(...) produisent des colonnes calculées qu'on ne peut pas filtrer directement dans un WHERE. La clause WITH résout élégamment ce problème.
WITH evolution AS (
SELECT
date_vente,
montant,
LAG(montant) OVER (ORDERBY date_vente) AS montant_precedent
FROM ventes
)
SELECT
date_vente,
montant,
montant - montant_precedent AS evolution
FROM evolution
WHERE montant_precedent ISNOT NULL;
Sans WITH, on serait obligé d'utiliser une sous-requête dans le FROM, ce qui est plus difficile à lire et à maintenir.
Performances : ce que dit la documentation
D'après la documentation officielle de PostgreSQL (v15), une CTE non matérialisée est traitée comme une sous-requête inline, ce qui laisse l'optimiseur l'intégrer au plan d'exécution global. Une CTE matérialisée est calculée une fois et mise en mémoire.
En pratique, pour des CTEs simples et peu volumineuses, la différence de performance est négligeable. Pour des CTEs sur des tables de plusieurs millions de lignes, il vaut mieux utiliser EXPLAIN ANALYZE pour vérifier le plan d'exécution réel.
La règle générale : privilégiez la lisibilité avec WITH, et optimisez avec EXPLAIN si vous observez un problème de performance.
Questions fréquentes
Quelle est la différence entre WITH et une vue SQL ?
Une vue est un objet permanent en base de données, accessible par n'importe quelle requête. Une CTE définie avec WITH est temporaire et ne vit que le temps de la requête. La vue nécessite des droits de création ; la CTE non.
Peut-on utiliser WITH dans un INSERT ou un UPDATE ?
Oui. La clause WITH peut précéder un INSERT, un UPDATE ou un DELETE dans la plupart des SGBD modernes. Par exemple, dans PostgreSQL, on peut écrire WITH données AS (...) INSERT INTO table SELECT * FROM données;. C'est utile pour préparer les données avant insertion.
Une CTE améliore-t-elle toujours les performances ?
Non. Une CTE améliore principalement la lisibilité. L'impact sur les performances dépend du SGBD, de la version, et du fait que la CTE soit matérialisée ou non. Mesurez toujours avec EXPLAIN ANALYZE avant de conclure.
Combien de CTEs peut-on enchaîner dans un seul bloc WITH ?
Il n'y a techniquement pas de limite fixée par le standard SQL. En pratique, au-delà de 5 à 6 CTEs, il vaut mieux envisager une vue ou une table temporaire pour maintenir la lisibilité et faciliter le débogage.
WITH RECURSIVE est-il différent de WITH ?
Oui. WITH RECURSIVE permet à une CTE de se référencer elle-même, ce qui est nécessaire pour parcourir des structures hiérarchiques (arborescences, graphes). La syntaxe et la logique sont différentes et font l'objet d'un traitement spécifique dans notre article sur les requêtes récursives SQL.
Conclusion
La clause WITH est l'un des outils les plus utiles du SQL moderne. Elle ne remplace pas les sous-requêtes ou les jointures, mais elle les rend beaucoup plus lisibles et maintenables dès que la logique dépasse quelques niveaux. En entretien data, l'écrire correctement du premier coup — bonne syntaxe, bonne structure, bonnes nominations — est un signal fort de maturité technique.
Entraînez-vous à réécrire vos sous-requêtes imbriquées avec WITH : vous verrez immédiatement la différence de clarté. Pour mettre ces connaissances en pratique sur des exercices réels, rendez-vous sur SQL Pratique et testez vos requêtes directement dans l'environnement en ligne.
Prêt à vous entraîner ?
50 exercices SQL interactifs avec éditeur en ligne, chronomètre et feedback IA.