Skip to main content
Cette section propose des guides pour configurer dbt et l’adaptateur ClickHouse, ainsi qu’un exemple d’utilisation de dbt avec ClickHouse à partir d’un jeu de données IMDB accessible au public. L’exemple couvre les étapes suivantes :
  1. Création d’un projet dbt et configuration de l’adaptateur ClickHouse.
  2. Définition d’un modèle.
  3. Mise à jour d’un modèle.
  4. Création d’un modèle incrémentiel.
  5. Création d’un modèle snapshot.
  6. Utilisation de vues matérialisées.
Ces guides sont conçus pour être utilisés conjointement avec le reste de la documentation, les fonctionnalités et configurations et la référence des matérialisations.

Configuration

Suivez les instructions de la section Configuration de dbt et de l’adaptateur ClickHouse pour préparer votre environnement. Important : ce qui suit a été testé avec Python 3.9.

Préparer ClickHouse

dbt excelle dans la modélisation de données fortement relationnelles. À titre d’exemple, nous fournissons un petit jeu de données IMDB avec le schéma relationnel suivant. Ce jeu de données provient du dépôt de jeux de données relationnelles. Il reste très simple par rapport aux schémas couramment utilisés avec dbt, mais constitue un exemple facile à manipuler : Nous utilisons un sous-ensemble de ces tables, comme indiqué. Créez les tables suivantes :
La colonne created_at de la table roles a pour valeur par défaut now(). Nous l’utilisons plus tard pour identifier les mises à jour incrémentales de nos modèles ; voir Modèles incrémentaux.
Nous utilisons la fonction s3 pour lire les données source depuis des endpoints publics afin d’insérer les données. Exécutez les commandes suivantes pour remplir les tables :
L’exécution de ces opérations peut varier selon votre bande passante, mais chacune ne devrait prendre que quelques secondes. Exécutez la requête suivante pour générer un récapitulatif de chaque acteur, classé par nombre d’apparitions dans des films, et pour confirmer que les données ont bien été chargées :
La réponse devrait ressembler à ceci :
Dans les guides suivants, nous convertirons cette requête en un modèle, en la matérialisant dans ClickHouse sous la forme d’une vue et d’une table dbt.

Connexion à ClickHouse

  1. Créez un projet dbt. Dans ce cas, nous le nommons d’après notre source imdb. Lorsque vous y êtes invité, sélectionnez clickhouse comme base de données source.
  2. Placez-vous dans le dossier de votre projet avec cd :
  3. À ce stade, vous aurez besoin de l’éditeur de texte de votre choix. Dans les exemples ci-dessous, nous utilisons le très répandu VS Code. En ouvrant le répertoire IMDB, vous devriez voir un ensemble de fichiers yml et sql :
  4. Mettez à jour votre fichier dbt_project.yml pour spécifier notre premier modèle, actor_summary, et définir le profil sur clickhouse_imdb.
  5. Nous devons ensuite fournir à dbt les informations de connexion de notre instance ClickHouse. Ajoutez le contenu suivant à votre fichier ~/.dbt/profiles.yml.
    Notez qu’il faut modifier l’utilisateur et le mot de passe. D’autres paramètres disponibles sont documentés ici.
  6. Depuis le répertoire IMDB, exécutez la commande dbt debug pour vérifier que dbt peut se connecter à ClickHouse.
    Vérifiez que la réponse inclut Connection test: [OK connection ok], ce qui indique que la connexion a réussi.

Création d’une matérialisation de vue simple

Lors de l’utilisation de la matérialisation de vue, un modèle est reconstruit sous forme de vue à chaque exécution, à l’aide d’une instruction CREATE VIEW AS dans ClickHouse. Cela ne nécessite aucun stockage de données supplémentaire, mais les requêtes seront plus lentes qu’avec des matérialisations en table.
  1. Dans le dossier imdb, supprimez le répertoire models/example :
  2. Créez un nouveau fichier dans actors, au sein du dossier models. C’est ici que nous créons les fichiers, chacun représentant un modèle d’acteur :
  3. Créez les fichiers schema.yml et actor_summary.sql dans le dossier models/actors.
    Le fichier schema.yml définit nos tables. Elles pourront ensuite être utilisées dans les macros. Modifiez models/actors/schema.yml pour qu’il contienne le contenu suivant :
    Le fichier actors_summary.sql définit notre modèle proprement dit. Notez que, dans la fonction config, nous demandons également que le modèle soit matérialisé en vue dans ClickHouse. Nos tables sont référencées depuis le fichier schema.yml via la fonction source ; par exemple, source('imdb', 'movies') fait référence à la table movies dans la base de données imdb. Modifiez models/actors/actors_summary.sql pour y mettre le contenu suivant :
    Remarquez que nous incluons la colonne updated_at dans notre actor_summary final. Nous l’utilisons ensuite pour les matérialisations incrémentielles.
  4. Dans le répertoire imdb, exécutez la commande dbt run.
  5. dbt représentera le modèle sous forme de vue dans ClickHouse, comme demandé. Nous pouvons maintenant interroger cette vue directement. Cette vue aura été créée dans la base de données imdb_dbt — cela est déterminé par le paramètre schema dans le fichier ~/.dbt/profiles.yml, sous le profil clickhouse_imdb.
    En interrogeant cette vue, nous pouvons reproduire les résultats de notre requête précédente avec une syntaxe plus simple :

Création d’une matérialisation en table

Dans l’exemple précédent, notre modèle a été matérialisé sous forme de vue. Bien que cela puisse offrir des performances suffisantes pour certaines requêtes, les requêtes SELECT plus complexes ou exécutées fréquemment gagnent parfois à être matérialisées sous forme de table. Cette matérialisation est utile pour les modèles qui seront interrogés par des outils de BI, afin d’offrir aux utilisateurs une expérience plus rapide. En pratique, les résultats de la requête sont alors stockés dans une nouvelle table, avec la surcharge de stockage associée : un INSERT TO SELECT est effectivement exécuté. Notez que cette table sera reconstruite à chaque exécution, c.-à-d. qu’elle n’est pas incrémentielle. De grands ensembles de résultats peuvent donc entraîner des temps d’exécution élevés — voir Limitations de dbt.
  1. Modifiez le fichier actors_summary.sql afin que le paramètre materialized soit défini sur table. Notez comment ORDER BY est défini, ainsi que l’utilisation du moteur de table MergeTree :
  2. Depuis le répertoire imdb, exécutez la commande dbt run. Cette exécution peut prendre un peu plus de temps — environ 10 s sur la plupart des machines.
  3. Confirmez la création de la table imdb_dbt.actor_summary :
    Vous devriez voir la table avec les types de données appropriés :
  4. Confirmez que les résultats de cette table sont cohérents avec les réponses précédentes. Notez l’amélioration sensible du temps de réponse maintenant que le modèle est une table :
    N’hésitez pas à exécuter d’autres requêtes sur ce modèle. Par exemple, quels acteurs ont les films les mieux classés avec plus de 5 apparitions ?

Création d’une matérialisation incrémentielle

L’exemple précédent a créé une table pour matérialiser le modèle. Cette table sera reconstruite à chaque exécution de dbt. Cela peut s’avérer irréalisable et extrêmement coûteux pour des jeux de résultats volumineux ou des transformations complexes. Pour remédier à ce problème et réduire le temps de build, dbt propose des matérialisations incrémentielles. Cela permet à dbt d’insérer ou de mettre à jour dans une table les enregistrements modifiés depuis la dernière exécution, ce qui convient bien aux données de type événementiel. En arrière-plan, une table temporaire est créée avec tous les enregistrements mis à jour, puis tous les enregistrements inchangés ainsi que les enregistrements mis à jour sont insérés dans une nouvelle table cible. Cela entraîne, pour les jeux de résultats volumineux, des limitations similaires à celles du modèle de table. Pour surmonter ces limitations sur de grands ensembles, l’adaptateur prend en charge le mode ‘inserts_only’, dans lequel toutes les mises à jour sont insérées dans la table cible sans créer de table temporaire (voir ci-dessous pour plus de détails). Pour illustrer cet exemple, nous allons ajouter l’acteur « Clicky McClickHouse », qui apparaîtra dans l’incroyable total de 910 films, ce qui lui garantira d’avoir joué dans encore plus de films que Mel Blanc.
  1. D’abord, nous modifions notre modèle pour qu’il soit de type incrémental. Cet ajout nécessite :
    1. unique_key - Pour garantir que l’adaptateur puisse identifier les lignes de manière unique, nous devons fournir une unique_key - dans ce cas, le champ id de notre requête suffit. Cela garantit l’absence de doublons de lignes dans notre table matérialisée. Pour plus de détails sur les contraintes d’unicité, voir ici.
    2. Filtre incrémental - Nous devons également indiquer à dbt comment identifier les lignes qui ont changé lors d’une exécution incrémentale. Pour cela, il faut fournir une expression delta. En général, cela implique un timestamp pour les données d’événements, d’où notre champ timestamp updated_at. Cette colonne, dont la valeur par défaut est now() lorsque des lignes sont insérées, permet d’identifier les nouveaux rôles. De plus, nous devons prendre en compte l’autre cas possible, où de nouveaux acteurs sont ajoutés. En utilisant la variable {{this}} pour désigner la table matérialisée existante, on obtient l’expression where id > (select max(id) from {{ this }}) or updated_at > (select max(updated_at) from {{this}}). Nous l’intégrons dans la condition {% if is_incremental() %}, ce qui garantit qu’elle n’est utilisée que lors des exécutions incrémentales, et non lors de la création initiale de la table. Pour plus de détails sur le filtrage des lignes pour les modèles incrémentaux, voir cette section de la documentation dbt.
    Mettez à jour le fichier actor_summary.sql comme suit :
    Notez que notre modèle ne réagira qu’aux mises à jour et aux ajouts apportés aux tables roles et actors. Pour prendre en compte toutes les tables, il est recommandé aux utilisateurs de scinder ce modèle en plusieurs sous-modèles, chacun avec ses propres critères incrémentiels. Ces modèles pourront ensuite être référencés et reliés entre eux. Pour plus de détails sur les références croisées entre modèles, consultez ici.
  2. Exécutez un dbt run et vérifiez les résultats de la table obtenue :
  3. Nous allons maintenant ajouter des données à notre modèle pour illustrer une mise à jour incrémentielle. Ajoutez l’acteur “Clicky McClickHouse” à la table actors :
  4. Faisons jouer « Clicky » dans 910 films choisis au hasard :
  5. Confirmez qu’il s’agit bien désormais de l’acteur comptant le plus d’apparitions en interrogeant la table source sous-jacente et en contournant tous les modèles dbt :
  6. Lancez un dbt run et vérifiez que notre modèle a bien été mis à jour et qu’il correspond aux résultats ci-dessus :

Fonctionnement interne

Nous pouvons identifier les instructions exécutées pour effectuer la mise à jour incrémentielle ci-dessus en interrogeant le query log de ClickHouse.
Adaptez la requête ci-dessus à la période d’exécution. Nous laissons l’examen du résultat à l’utilisateur, mais mettons en évidence la stratégie générale utilisée par l’adaptateur pour effectuer des mises à jour incrémentielles :
  1. L’adaptateur crée une table temporaire actor_sumary__dbt_tmp. Les lignes modifiées y sont envoyées.
  2. Une nouvelle table, actor_summary_new, est créée. Les lignes de l’ancienne table sont ensuite envoyées de l’ancienne vers la nouvelle, en vérifiant que leurs identifiants n’existent pas dans la table temporaire. Cela permet de gérer efficacement les mises à jour et les doublons.
  3. Les résultats de la table temporaire sont envoyés vers la nouvelle table actor_summary :
  4. Enfin, la nouvelle table est échangée de manière atomique avec l’ancienne version via une instruction EXCHANGE TABLES. L’ancienne table et la table temporaire sont ensuite supprimées.
Le schéma ci-dessous l’illustre : Cette stratégie peut poser des difficultés sur de très grands modèles. Pour plus de détails, voir Limitations.

Stratégie append (mode insertions uniquement)

Pour surmonter les limites des grands jeux de données dans les modèles incrémentiels, l’adaptateur utilise le paramètre de configuration dbt incremental_strategy. Il peut être défini sur la valeur append. Dans ce cas, les lignes mises à jour sont insérées directement dans la table cible (autrement dit imdb_dbt.actor_summary) et aucune table temporaire n’est créée. Remarque : le mode append uniquement nécessite que vos données soient immuables ou que les doublons soient acceptables. Si vous voulez un modèle de table incrémentielle qui prend en charge les lignes modifiées, n’utilisez pas ce mode ! Pour illustrer ce mode, nous allons ajouter un nouvel acteur et réexécuter dbt run avec incremental_strategy='append'.
  1. Configurez le mode append uniquement dans actor_summary.sql :
  2. Ajoutons un autre acteur célèbre : Danny DeBito
  3. Faisons jouer Danny dans 920 films pris au hasard.
  4. Exécutez dbt run et confirmez que Danny a bien été ajouté à la table actor_summary
Notez à quel point cette exécution incrémentielle a été plus rapide que l’insertion de « Clicky ». En vérifiant à nouveau la table query_log, on voit les différences entre les 2 exécutions incrémentielles :
Pour cette exécution, seules les nouvelles lignes sont ajoutées directement à la table imdb_dbt.actor_summary, sans création de table.

Mode suppression et insertion (expérimental)

Historiquement, ClickHouse n’offrait qu’une prise en charge limitée des mises à jour et des suppressions, sous la forme de mutations. Celles-ci peuvent être extrêmement gourmandes en IO et doivent généralement être évitées. ClickHouse 22.8 a introduit les suppressions légères, et ClickHouse 25.7 les mises à jour légères. Avec l’introduction de ces fonctionnalités, les modifications effectuées par une seule requête de mise à jour, même lorsqu’elles sont matérialisées de façon asynchrone, prennent effet instantanément du point de vue de l’utilisateur. Ce mode peut être configuré pour un modèle via le paramètre incremental_strategy, c.-à-d.
Cette stratégie s’exécute directement sur la table du modèle cible. Ainsi, si un problème survient pendant l’opération, les données du modèle incrémental risquent fort de se retrouver dans un état incohérent : il n’y a pas de mise à jour atomique. En résumé, cette approche :
  1. L’adaptateur crée une table temporaire actor_sumary__dbt_tmp. Les lignes modifiées y sont envoyées.
  2. Un DELETE est exécuté sur la table actor_summary actuelle. Les lignes sont supprimées par id à partir de actor_sumary__dbt_tmp
  3. Les lignes de actor_sumary__dbt_tmp sont insérées dans actor_summary à l’aide de INSERT INTO actor_summary SELECT * FROM actor_sumary__dbt_tmp.
Ce processus est illustré ci-dessous :

Mode insert_overwrite (expérimental)

Effectue les étapes suivantes :
  1. Crée une table de staging (temporaire) ayant la même structure que la relation du modèle incrémental : CREATE TABLE {staging} AS {target}.
  2. Insère uniquement les nouveaux enregistrements (produits par SELECT) dans la table de staging.
  3. Remplace uniquement les nouvelles partitions (présentes dans la table de staging) dans la table cible.

Cette approche présente les avantages suivants :
  • Elle est plus rapide que la stratégie par défaut, car elle ne copie pas l’intégralité de la table.
  • Elle est plus sûre que les autres stratégies, car elle ne modifie pas la table d’origine tant que l’opération INSERT n’a pas abouti : en cas d’échec intermédiaire, la table d’origine n’est pas modifiée.
  • Elle applique la bonne pratique d’ingénierie des données dite de « l’immuabilité des partitions », ce qui simplifie le traitement incrémental et parallèle des données, les retours arrière, etc.

Créer un snapshot

Les snapshots dbt permettent de conserver une trace des modifications apportées au fil du temps à un modèle mutable. Cela permet ensuite d’exécuter des requêtes à un instant donné sur des modèles, afin que les analystes puissent “remonter dans le temps” jusqu’à l’état précédent d’un modèle. Cela est réalisé à l’aide de dimensions à évolution lente de type 2, où des colonnes de date de début et de fin enregistrent le moment où une ligne était valide. Cette fonctionnalité est prise en charge par l’adaptateur ClickHouse et est illustrée ci-dessous. Cet exemple suppose que vous avez terminé Créer un modèle de table incrémental. Assurez-vous que votre actor_summary.sql ne définit pas inserts_only=True. Votre models/actor_summary.sql doit ressembler à ceci :
  1. Créez un fichier actor_summary dans le répertoire snapshots.
  2. Mettez à jour le contenu du fichier actor_summary.sql avec le contenu suivant :
Quelques remarques concernant ce contenu :
  • La requête select définit les résultats dont vous souhaitez capturer un snapshot au fil du temps. La fonction ref est utilisée pour faire référence à notre modèle actor_summary créé précédemment.
  • Nous avons besoin d’une colonne timestamp pour indiquer les modifications des enregistrements. Notre colonne updated_at (voir Création d’un modèle de table Incremental) peut être utilisée ici. Le paramètre strategy indique que nous utilisons un timestamp pour signaler les mises à jour, tandis que le paramètre updated_at précise la colonne à utiliser. Si ce champ n’est pas présent dans votre modèle, vous pouvez également utiliser la stratégie check. Cette approche est nettement moins efficace et oblige l’utilisateur à spécifier une liste de colonnes à comparer. dbt compare les valeurs actuelles et historiques de ces colonnes, en enregistrant toute modification (ou en ne faisant rien si elles sont identiques).
  1. Exécutez la commande dbt snapshot.
Notez que la table actor_summary_snapshot a été créée dans la base de données snapshots (déterminée par le paramètre target_schema).
  1. Si vous examinez un échantillon de ces données, vous verrez que dbt a inclus les colonnes dbt_valid_from et dbt_valid_to. Cette dernière contient des valeurs null. Les exécutions suivantes le mettront à jour.
  2. Faites apparaître notre acteur préféré, Clicky McClickHouse, dans 10 films supplémentaires.
  3. Relancez la commande dbt run depuis le répertoire imdb. Cela mettra à jour le modèle incrémental. Une fois l’opération terminée, exécutez dbt snapshot pour capturer les modifications.
  4. Si nous interrogeons maintenant notre instantané, nous constatons que nous avons 2 lignes pour Clicky McClickHouse. Notre entrée précédente a désormais une valeur dbt_valid_to. Notre nouvelle valeur est enregistrée avec la même valeur dans la colonne dbt_valid_from, et une valeur dbt_valid_to égale à null. Si nous avions de nouvelles lignes, elles seraient également ajoutées à l’instantané.
Pour en savoir plus sur les snapshots dbt, consultez cette page.

Utiliser les seeds

dbt permet de charger des données à partir de fichiers CSV. Cette fonctionnalité n’est pas adaptée au chargement d’exports volumineux d’une base de données et convient davantage à de petits fichiers, généralement utilisés pour des tables de codes et des dictionnaires, par exemple pour faire correspondre des codes pays à des noms de pays. Pour un exemple simple, nous générons puis importons une liste de codes de genre à l’aide de la fonctionnalité de seed.
  1. Nous générons une liste de codes de genre à partir de notre jeu de données existant. Depuis le répertoire dbt, utilisez clickhouse-client pour créer un fichier seeds/genre_codes.csv :
  2. Exécutez la commande dbt seed. Cela créera une nouvelle table genre_codes dans notre base de données imdb_dbt (comme défini par notre configuration de schéma) avec les lignes de notre fichier CSV.
  3. Vérifiez qu’elles ont bien été chargées :

Informations complémentaires

Les guides précédents ne font qu’effleurer les fonctionnalités de dbt. Il est recommandé aux utilisateurs de consulter l’excellente documentation de dbt.
Dernière modification le 24 juillet 2026