Passer au contenu principal
ClickHouse prend en charge les expressions de table communes (CTE), les expressions scalaires communes et les requêtes récursives.

Expressions de table communes

Les expressions de table communes représentent des sous-requêtes nommées. Elles peuvent être référencées par leur nom partout dans une requête SELECT où une expression de table est autorisée. Les sous-requêtes nommées peuvent être référencées par leur nom dans la portée de la requête courante ou dans les portées des sous-requêtes enfants. Chaque référence à une expression de table commune dans une requête SELECT est toujours remplacée par la sous-requête de sa définition si la CTE n’est pas explicitement définie comme matérialisée (voir Expressions de table communes matérialisées). La récursivité est évitée en masquant la CTE courante du processus de résolution des identifiants. Veuillez noter que les CTE ne garantissent pas les mêmes résultats partout où elles sont appelées, car la requête est réexécutée à chaque utilisation.

Syntaxe

Exemple

Voici un exemple de cas où une sous-requête est réexécutée :
Si les CTE transmettaient exactement les résultats, et pas seulement un morceau de code, vous verriez toujours 1000000 Cependant, comme nous faisons référence deux fois à cte_numbers, des nombres aléatoires sont générés à chaque fois et nous obtenons donc des résultats aléatoires différents : 280501, 392454, 261636, 196227, etc.

Expressions de table communes matérialisées

Par défaut, ClickHouse intègre la sous-requête d’une CTE à chaque référence, et la réexécute donc à chaque fois. L’ajout du mot-clé MATERIALIZED indique à ClickHouse d’exécuter la sous-requête de la CTE exactement une fois, de stocker les résultats dans une table temporaire, puis d’utiliser cette table pour toutes les références. Cela est particulièrement utile lorsque la même CTE est référencée plusieurs fois dans une requête (par exemple, dans des auto-jointures ou plusieurs sous-requêtes IN), car le calcul sous-jacent n’est effectué qu’une seule fois.
Les CTE matérialisées sont une fonctionnalité expérimentale. Elles nécessitent que l’analyseur et le paramètre enable_materialized_cte soient activés.

Syntaxe

Quand utiliser

Les CTE matérialisées sont particulièrement utiles dans les cas suivants :
  • La même CTE est référencée plus d’une fois dans une requête. Sans MATERIALIZED, chaque référence réexécute la sous-requête de manière indépendante.
  • La CTE contient des fonctions non déterministes comme generateRandom. La matérialisation garantit que toutes les références voient les mêmes données.
  • La CTE implique des calculs coûteux (agrégations, jointures, lecture de grands volumes de données) qu’il ne faut pas répéter.
Si une CTE matérialisée n’est référencée qu’une seule fois, ClickHouse la réintègre automatiquement sous forme de sous-requête classique afin d’éviter tout surcoût inutile.

Exemples

Exemple 1 : Auto-jointure sur une CTE matérialisée Sans MATERIALIZED, les deux côtés de la jointure exécuteraient la sous-requête indépendamment. Avec MATERIALIZED, la table n’est parcourue qu’une seule fois et les deux côtés de la jointure lisent dans la même table temporaire.
Exemple 2 : Résultats déterministes avec des fonctions non déterministes Les CTE classiques avec generateRandom produisent des résultats différents à chaque référence. La matérialisation de la CTE garantit la cohérence :
Comme les deux références s’appuient sur les mêmes données matérialisées, le résultat est toujours 1000000. Exemple 3 : Chaînage de CTE matérialisées Les CTE matérialisées peuvent faire référence à d’autres CTE matérialisées. ClickHouse résout les dépendances et les matérialise dans le bon ordre :
L’ordre des définitions de CTE n’a pas d’importance — il est possible de faire référence à des CTE définies plus loin :

Restrictions

  • Paramètre expérimental requis : le paramètre enable_materialized_cte doit être activé.
  • Analyseur requis : les CTE matérialisées fonctionnent uniquement lorsque l’analyseur est activé (enable_analyzer = 1).
  • Non pris en charge avec RECURSIVE : la combinaison des mots-clés MATERIALIZED et RECURSIVE n’est pas autorisée et entraîne une exception UNSUPPORTED_METHOD.
  • Les CTE corrélées sont interdites : une CTE matérialisée ne peut pas référencer des colonnes provenant de portées de requête englobantes.

Expressions scalaires communes

ClickHouse vous permet de déclarer des alias pour des expressions scalaires arbitraires dans la clause WITH. Les expressions scalaires communes peuvent être référencées n’importe où dans la requête.
Si une expression scalaire commune fait référence à autre chose qu’un littéral constant, elle peut entraîner la présence de variables libres. ClickHouse résout chaque identifiant dans la portée la plus proche possible, ce qui signifie que des variables libres peuvent faire référence à des entités inattendues en cas de conflit de noms, ou conduire à une sous-requête corrélée. Il est recommandé de définir une CSE sous forme de fonction lambda (possible uniquement lorsque l’analyseur est activé), en liant tous les identifiants utilisés afin d’obtenir un comportement plus prévisible lors de la résolution des identifiants d’expression.

Syntaxe

Exemples

Exemple 1 : Utiliser une expression constante comme “variable”
Exemple 2 : Utiliser des fonctions d’ordre supérieur pour lier les identifiants
Exemple 3 : Utilisation de fonctions d’ordre supérieur avec des variables libres Les requêtes d’exemple suivantes montrent que les identifiants non liés sont résolus vers une entité dans la portée la plus proche. Ici, extension n’est pas liée dans le corps de la fonction lambda gen_name. Bien que extension soit définie comme '.txt' en tant qu’expression scalaire commune dans la portée de définition et d’utilisation de generated_names, elle est résolue comme une colonne de la table extension_list, car elle est disponible dans la sous-requête generated_names.
Exemple 4 : Retrait du résultat de l’expression sum(bytes) de la liste des colonnes de la clause SELECT
Exemple 5 : Utilisation du résultat d’une sous-requête scalaire
Exemple 6 : Réutilisation d’une expression dans une sous-requête

Requêtes récursives

Le modificateur RECURSIVE, facultatif, permet à une requête WITH de faire référence à son propre résultat. Exemple : Exemple : Additionner les entiers de 1 à 100
Les CTE récursifs s’appuient sur l’analyseur de requêtes, introduit dans la version 24.3. Si vous utilisez la version 24.3+ et rencontrez une exception (UNKNOWN_TABLE) ou (UNSUPPORTED_METHOD), cela indique que l’analyseur est désactivé sur votre instance, votre rôle ou votre profil. Pour activer l’analyseur, activez le paramètre allow_experimental_analyzer ou mettez à jour le paramètre compatibility vers une version plus récente. À partir de la version 24.8, l’analyseur est pleinement passé en production, et le paramètre allow_experimental_analyzer a été renommé en enable_analyzer.
La forme générale d’une requête récursive WITH est toujours la suivante : un terme non récursif, puis UNION ALL, puis un terme récursif, seul ce dernier pouvant contenir une référence à la propre sortie de la requête. Une requête CTE récursive s’exécute comme suit :
  1. Évaluez le terme non récursif. Placez le résultat de cette requête dans une table de travail temporaire.
  2. Tant que la table de travail n’est pas vide, répétez ces étapes :
    1. Évaluez le terme récursif en remplaçant l’auto-référence récursive par le contenu actuel de la table de travail. Placez le résultat de cette requête dans une table intermédiaire temporaire.
    2. Remplacez le contenu de la table de travail par celui de la table intermédiaire, puis videz la table intermédiaire.
Les requêtes récursives sont généralement utilisées pour manipuler des données hiérarchiques ou structurées en arbre. Par exemple, nous pouvons écrire une requête qui effectue un parcours d’arbre : Exemple : Parcours d’arbre Commençons par créer la table de l’arbre :
Nous pouvons parcourir ces arbres à l’aide de la requête suivante : Exemple : Parcours d’arbre

Ordre de parcours

Pour établir un parcours en profondeur, nous calculons pour chaque ligne de résultat un tableau des lignes déjà visitées : Exemple : Parcours d’arbre en profondeur
Pour obtenir un parcours en largeur, l’approche standard consiste à ajouter une colonne qui indique la profondeur de la recherche : Exemple : parcours d’arbre en largeur

Détection des cycles

Commençons par créer la table du graphe :
Nous pouvons parcourir ce graphe avec la requête suivante : Exemple : Parcours du graphe sans détection de cycles
Mais si l’on ajoute un cycle dans ce graphe, la requête précédente échouera avec l’erreur Maximum recursive CTE evaluation depth :
La méthode standard pour gérer les cycles consiste à calculer un tableau contenant les nœuds déjà visités : Exemple : parcours du graphe avec détection de cycles

Requêtes infinies

Il est également possible d’utiliser des requêtes CTE récursives infinies si LIMIT est utilisé dans la requête externe : Exemple : Requête CTE récursive infinie

Virgule finale

Une virgule est autorisée après le dernier élément de la clause WITH :
Dernière modification le 2 juillet 2026