Une CTE, c'est une sous-requête nommée
Une CTE (Common Table Expression) en SQLite, c'est tout simplement une sous-requête que l'on extrait pour lui donner un nom. Plutôt que d'imbriquer un SELECT dans un autre SELECT, on la définit en tête de requête avec WITH, on lui attribue un nom, puis on l'utilise dans la requête principale comme s'il s'agissait d'une table.
La structure reste toujours la même :
Lisez-le de haut en bas : on construit d'abord un résultat nommé customer_totals, puis on l'interroge. La CTE se comporte comme une vue temporaire qui n'existe que le temps de cette unique requête.
La même requête sans CTE
Voici la même logique écrite sous forme de sous-requête, histoire de voir ce que la CTE vient remplacer :
Même résultat. Mais regardez bien l'ordre de lecture : votre œil doit plonger dans les parenthèses, comprendre ce qui est calculé, puis remonter. La version avec CTE se lit dans l'ordre où le travail se fait — on définit le résultat intermédiaire, puis on s'en sert. Sur une petite requête, ça revient au même. Sur une requête à trois ou quatre étapes, c'est la différence entre un code qu'on parcourt d'un coup d'œil et un code qu'il faut déchiffrer.
Plusieurs CTE dans une même requête
Vous pouvez enchaîner plusieurs CTE en les séparant par des virgules. Chacune peut référencer celles définies avant elle, ce qui permet de construire un pipeline d'étapes nommées :
Un seul WITH, puis les définitions de CTE séparées par des virgules. Le deuxième CTE (big_spenders) lit le premier (customer_totals) exactement comme s'il s'agissait d'une table. Le SELECT principal vient après la dernière définition de CTE.
Petit piège classique : remettre un WITH devant le deuxième CTE. Surtout pas — c'est une erreur de syntaxe. Un seul WITH suffit pour tous.
Réutiliser un CTE plusieurs fois
C'est précisément là que les CTE prennent l'avantage sur les sous-requêtes. Si le même résultat intermédiaire vous sert à deux endroits, un CTE vous permet de le calculer une seule fois et d'y faire référence autant de fois que nécessaire :
La CTE est utilisée deux fois : une première fois pour calculer la moyenne, puis une seconde comme source principale. Sans la CTE, il faudrait dupliquer la requête GROUP BY, et la moindre modification devrait être répercutée à deux endroits.
Utiliser une CTE avec INSERT, UPDATE et DELETE
Les CTE ne se limitent pas au SELECT. Tu peux placer une clause WITH devant un INSERT, un UPDATE ou un DELETE pour exploiter une sous-requête nommée dans une opération d'écriture :
La CTE indique quelles lignes marquer. Le INSERT ... SELECT s'en sert comme source. La même astuce marche aussi avec DELETE FROM ... WHERE id IN (SELECT id FROM cte) pour des suppressions par étapes lorsque la logique de ciblage devient touffue.
Quand sortir une CTE
Quelques repères :
- La requête comporte plusieurs étapes logiques. Agréger, puis filtrer sur l'agrégat, puis joindre le résultat — c'est un pipeline, et une CTE par étape rend le tout bien plus lisible.
- Vous alliez répéter la même sous-requête. Définissez-la une fois, référencez-la deux fois.
- La sous-requête mérite un nom. Si vous étiez sur le point d'écrire un commentaire au-dessus de la sous-requête pour expliquer ce qu'elle représente, le nom de la CTE est ce commentaire — et la syntaxe vous oblige à le poser.
- Vous vous apprêtez à écrire une requête récursive. Ce n'est possible qu'avec
WITH RECURSIVE, sujet de la page suivante.
Quand ne pas s'embêter :
- Une seule sous-requête simple utilisée à un seul endroit.
WHERE id IN (SELECT id FROM ...)fait très bien le boulot tel quel. - Des requêtes critiques en performance où vous avez déjà vérifié qu'inliner la logique aide. SQLite traite généralement une CTE comme une barrière d'optimisation moins stricte que certaines autres bases, mais sur les chemins chauds, mieux vaut vérifier avec
EXPLAIN QUERY PLAN.
Un exemple concret
Mettons tout cela bout à bout — un petit rapport qui trouve, pour chaque client, sa plus grosse commande et la compare à sa moyenne :
Deux CTE, chacune avec un rôle bien précis. Le SELECT final se charge de mettre en forme le résultat. La requête se lit de haut en bas, étape par étape — et c'est exactement ce qui fait tout l'intérêt des CTE.
La suite : les CTE récursives
Jusqu'ici, on est restés sur des CTE classiques — une sous-requête nommée, évaluée une seule fois. Mais SQLite propose aussi WITH RECURSIVE, où une CTE s'appelle elle-même pour parcourir des hiérarchies, générer des séries ou explorer des graphes. C'est ce qu'on verra sur la page suivante.
Questions fréquentes
C'est quoi une CTE en SQLite ?
Une Common Table Expression, c'est une sous-requête nommée que l'on place en tête d'un SELECT, INSERT, UPDATE ou DELETE. On l'introduit avec le mot-clé WITH, on lui donne un nom, puis on s'y réfère dans la requête principale comme s'il s'agissait d'une table. L'intérêt : rendre les requêtes complexes lisibles en construisant le résultat étape par étape.
Quelle différence entre une CTE et une sous-requête en SQLite ?
Le résultat peut être strictement identique — une CTE n'est rien d'autre qu'une sous-requête qu'on a sortie et nommée. La vraie différence se joue sur la lisibilité et la réutilisation : une CTE peut être appelée plusieurs fois dans la même requête, et son nom documente ce que représente le résultat intermédiaire. Pour un simple filtre ponctuel, une sous-requête suffit ; dès qu'il y a plusieurs étapes logiques, la CTE prend l'avantage.
Peut-on enchaîner plusieurs CTE dans une même requête SQLite ?
Oui, sans problème. Après le premier WITH, on sépare les CTE suivantes par des virgules — pas besoin de répéter WITH. Chaque CTE peut s'appuyer sur celles définies avant elle, ce qui permet de construire un véritable pipeline d'étapes nommées. Le SELECT principal vient ensuite, après la dernière CTE.