ITSkillsCenter
Blog

Analytics local avec dbt et DuckDB : tutoriel pas à pas

11 min de lecture

Les data warehouses cloud (Snowflake, BigQuery, Redshift) facturent à l’usage et la facture grimpe vite. Pour des volumes inférieurs à quelques centaines de gigaoctets — la majorité des projets analytiques d’entreprise moyenne — une alternative locale s’est imposée depuis 2023 : DuckDB pour l’exécution SQL analytique, dbt-core pour la modélisation et la qualité. Cette combinaison offre une stack analytique complète, gratuite, qui tourne entièrement sur un poste de travail ou un VPS modeste. Ce tutoriel construit pas-à-pas un projet dbt+DuckDB depuis zéro : initialisation, modèles SQL, tests, documentation, exécution programmée.

Pour le contexte global, voir le guide principal sur la stack data 2026. Pour des aspects voisins, voir aussi le tutoriel approfondi sur dbt-core et celui sur l’installation de DuckDB sur VPS.

Prérequis

  • Python 3.10+, pip
  • dbt-duckdb 1.9+, duckdb 1.x
  • Connaissance SQL (SELECT, JOIN, GROUP BY, CTE)
  • Quelques fichiers Parquet ou CSV à modéliser
  • Temps estimé : 2 à 3 heures

Étape 1 — Pourquoi cette combinaison

DuckDB est une base analytique embarquée, équivalent fonctionnel de SQLite mais orientée colonnes et optimisée pour les agrégations sur de gros volumes. Sur un poste récent, DuckDB lit 100 millions de lignes Parquet et exécute un GROUP BY avec jointures en quelques secondes. Aucun serveur à administrer, aucune licence — un fichier .duckdb ou des fichiers Parquet directement requêtés en SQL.

dbt-core est l’outil de modélisation : on écrit des transformations SQL versionnées dans Git, dbt construit le DAG d’exécution, lance les tests qualité, et génère une documentation web. Le couple DuckDB + dbt offre une stack analytique complète, gratuite, et reproductible. Pour des volumes au-delà de quelques To, on basculerait sur Snowflake ou BigQuery — mais c’est rare en pratique sur les projets PME ou indépendants.

Étape 2 — Installer dbt-duckdb

L’adaptateur dbt-duckdb installe à la fois dbt-core et le moteur DuckDB sous-jacent. Une seule commande suffit pour avoir un environnement opérationnel. On vérifie ensuite avec dbt --version qui affiche les versions du core et de l’adaptateur.

python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install dbt-duckdb
dbt --version

La sortie liste deux numéros : Core et duckdb. Si l’installation échoue avec un conflit de dépendances, c’est presque toujours qu’un autre adaptateur dbt est déjà présent dans l’environnement — créer un venv frais règle le problème. À ce stade, on n’a encore aucun projet ; on en initialise un dans la prochaine étape.

Étape 3 — Initialiser le projet dbt

La commande dbt init génère un squelette complet : dossiers models/, tests/, macros/, fichier de configuration dbt_project.yml, et profil de connexion dans ~/.dbt/profiles.yml. Pour DuckDB, le profil est trivial — pas d’utilisateur, pas de mot de passe, juste un chemin de fichier.

dbt init analytics
cd analytics
ls -la

Le wizard demande le nom du profil et le chemin de la base DuckDB. On accepte les valeurs par défaut. Le résultat est un projet utilisable immédiatement : un modèle d’exemple my_first_dbt_model.sql est même fourni pour valider la chaîne. Avant de l’exécuter, on personnalise le profil pour pointer vers le bon fichier DuckDB.

Étape 4 — Configurer le profil de connexion

Le fichier ~/.dbt/profiles.yml contient les paramètres de connexion à la base. Pour DuckDB, on précise simplement le chemin du fichier qui sera créé automatiquement à la première exécution. Le mode local convient aux développeurs ; en CI on utilisera plutôt un fichier temporaire ou un volume monté.

# ~/.dbt/profiles.yml
analytics:
  target: dev
  outputs:
    dev:
      type: duckdb
      path: ./analytics.duckdb
      threads: 4
      extensions:
        - httpfs
        - parquet

Les extensions httpfs et parquet permettent de lire des fichiers Parquet directement, y compris depuis S3 ou un autre stockage HTTP — fonctionnalité distinctive de DuckDB qui évite des étapes d’ingestion. threads: 4 autorise l’exécution parallèle de modèles indépendants ; on l’ajuste selon le nombre de cœurs disponibles.

Étape 5 — Créer un premier modèle staging

L’architecture standard dbt sépare les modèles en couches : staging (nettoie les sources brutes), intermediate (jointures et calculs intermédiaires), et marts (tables finales métier). On commence par le staging : il lit la source brute (un fichier Parquet par exemple) et la nettoie en SQL — types corrigés, colonnes renommées, valeurs aberrantes filtrées.

-- models/staging/stg_ventes.sql
{{ config(materialized='view') }}

with source as (
    select * from read_parquet('data/ventes_*.parquet')
),

renomme as (
    select
        id_transaction        as transaction_id,
        date_commande::date   as date,
        upper(canal_paiement) as canal,
        montant               as montant_eur,
        nb_articles
    from source
    where montant > 0
)

select * from renomme

La fonction read_parquet de DuckDB lit directement les fichiers Parquet sans étape d’import. Le glob ventes_*.parquet fonctionne nativement. materialized='view' indique à dbt de matérialiser ce modèle comme une vue (rapide à reconstruire, ne consomme pas d’espace disque) plutôt qu’une table — c’est le bon choix pour le staging qui change souvent en développement.

Étape 6 — Construire un modèle mart

Au-dessus du staging, on construit les tables finales que les utilisateurs métier requêteront. Ici, un agrégat par jour et par canal de paiement. La référence {{ ref('stg_ventes') }} indique à dbt de chaîner les modèles : il créera d’abord la vue de staging, puis cette table. Le DAG est construit automatiquement.

-- models/marts/fct_ventes_journalier.sql
{{ config(materialized='table') }}

select
    date,
    canal,
    count(*)              as nb_transactions,
    sum(montant_eur)      as ca,
    avg(montant_eur)      as panier_moyen,
    sum(nb_articles)      as articles_vendus
from {{ ref('stg_ventes') }}
group by date, canal
order by date, canal

Le mart est en table physique parce qu’il est requêté souvent et que sa reconstruction prend du temps. dbt sait qu’il doit reconstruire ce modèle dès que stg_ventes change ; cette gestion automatique des dépendances est exactement ce qui rend dbt précieux. On lance la construction avec dbt run qui exécute toute la chaîne.

Étape 7 — Ajouter des tests qualité

Les tests dbt vérifient les invariants des données : telle colonne est unique, telle autre n’est jamais nulle, les valeurs d’une catégorie sont restreintes à une liste. dbt fournit quatre tests génériques natifs (unique, not_null, accepted_values, relationships) déclarés dans un YAML par modèle. Lancer dbt test exécute tous les tests et signale les violations.

# models/staging/_staging.yml
version: 2

models:
  - name: stg_ventes
    columns:
      - name: transaction_id
        tests:
          - unique
          - not_null
      - name: canal
        tests:
          - accepted_values:
              values: ["CARTE", "ESPECES", "MOBILE", "VIREMENT"]
      - name: montant_eur
        tests:
          - not_null

Si le test unique échoue, c’est qu’il y a des doublons sur l’identifiant de transaction — soit un bug d’ingestion, soit un changement légitime du modèle métier qui demande de retirer la contrainte. Si accepted_values échoue, c’est qu’une nouvelle modalité de paiement est apparue et qu’il faut la déclarer. Cette boucle de tests rend la maintenance d’un projet analytique sereine, là où les pipelines SQL ad hoc dérivent silencieusement.

Étape 8 — Documentation automatique

dbt génère automatiquement une documentation web : DAG des modèles, descriptions, colonnes typées, lineage entre sources et marts. Ajouter une description par modèle dans le YAML alimente cette documentation. Pour un projet de quelques dizaines de modèles, c’est l’unique endroit où l’équipe va se référer pour comprendre les transformations.

dbt docs generate
dbt docs serve --port 8080

La première commande compile la documentation à partir du code SQL et des YAML. La seconde lance un mini-serveur web sur le port 8080. Le navigateur affiche un graphe interactif des modèles : on clique sur un nœud pour voir son SQL, ses colonnes, ses dépendances amont et aval. C’est la meilleure documentation possible — elle reste toujours à jour parce qu’elle est générée à partir du code.

Étape 9 — Sources et freshness

Les sources formalisent les fichiers ou tables d’entrée du projet. On les déclare dans un YAML avec leur emplacement et, optionnellement, des tests de fraîcheur (la dernière donnée doit dater de moins de 24 h par exemple). C’est ce qui transforme dbt d’un outil de transformation en un outil de qualité de bout en bout.

# models/sources.yml
version: 2

sources:
  - name: brut
    schema: main
    tables:
      - name: ventes
        # external_location est l'option dbt-duckdb qui pointe la source
        # vers des fichiers Parquet directement requêtables en SQL via read_parquet.
        # Elle remplace le couple identifier/external schema utilisé sur
        # les warehouses classiques.
        meta:
          external_location: "read_parquet('data/ventes_*.parquet')"
        loaded_at_field: date_commande
        freshness:
          warn_after:  {count: 24, period: hour}
          error_after: {count: 48, period: hour}

La commande dbt source freshness teste si les données sont à jour. Si la dernière ligne de date_commande dépasse 24 h, on a un avertissement ; au-delà de 48 h, une erreur. La clé meta.external_location est l’option spécifique à l’adaptateur dbt-duckdb : elle indique à dbt comment requêter la source — ici via read_parquet, ce qui rend le glob de fichiers utilisable comme s’il s’agissait d’une table. Couplée à un cron, cette vérification alerte automatiquement quand le pipeline d’ingestion en amont est en panne, sans attendre qu’un utilisateur métier signale qu’il manque des données.

Étape 10 — Automatiser l’exécution

Une fois le projet stabilisé, on l’exécute automatiquement. Pour un usage local, un cron quotidien suffit. Pour une production plus ambitieuse, un orchestrateur comme Dagster, Airflow ou Prefect appelle dbt comme une étape de pipeline plus large. dbt expose une CLI parfaitement scriptable et un mode JSON pour parser les résultats.

# refresh.sh
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
source .venv/bin/activate
# dbt deps : à n'ajouter que si le projet importe des packages via packages.yml
dbt source freshness || true   # ne pas planter si freshness en warning
dbt run
dbt test

Programmé dans crontab via 0 6 * * * /chemin/refresh.sh >> /var/log/dbt.log 2>&1, le script tourne tous les matins à 6 h. Le set -euo pipefail garantit qu’une erreur dans une étape arrête le script. Pour notifier en cas d’échec, on ajoute en queue un appel à un webhook Slack ou à un mail. C’est l’orchestration la plus simple qui couvre 80 % des besoins en attendant un Dagster ou Airflow plus tard.

Erreurs fréquentes

ErreurCauseSolution
dbt ne trouve pas les fichiers ParquetChemin relatif depuis le mauvais dossierLancer dbt depuis la racine du projet, ou utiliser un chemin absolu
Modèle materialisé en table mais lentPas d’index ou pas de partitionnementDuckDB n’a pas d’index ; partitionner via clustering sur les colonnes filtrées
Conflit de versions dbtdbt-core et adaptateur désynchroniséspip install –upgrade dbt-duckdb (l’adaptateur tire le bon core)
Tests qui n’échouent jamaisYAML mal indenté, tests ignorésdbt test doit lister les tests exécutés ; sinon corriger l’indentation
Documentation videdbt docs generate non lancé après modificationRégénérer après chaque ajout de modèle ou description
Permission denied sur analytics.duckdbDeux processus dbt concurrentsDuckDB est mono-écrivain : sérialiser les exécutions
read_parquet échoue sur S3Extension httpfs non chargéeAjouter httpfs dans extensions du profil

Tutoriels associés

Ressources officielles

FAQ

DuckDB ou SQLite ?

SQLite pour des écritures fréquentes par enregistrement, OLTP léger. DuckDB pour l’analytique : agrégations, scans massifs, jointures. Les deux sont embarqués sans serveur ; ils répondent à des cas d’usage opposés.

Jusqu’à quelle volumétrie ?

Confortable jusqu’à 100-200 Go. Au-delà, on regarde Snowflake ou BigQuery. Pour des To, le calcul distribué devient nécessaire — DuckDB peut tout de même être utilisé en lecture sur fichiers Parquet partitionnés s’ils tiennent sur disque local.

dbt-cloud ou dbt-core ?

dbt-core est gratuit et open source ; on l’orchestre soi-même. dbt-cloud est l’offre SaaS payante avec interface web, alerting et intégration Git. Pour un projet personnel ou PME, dbt-core suffit largement.

Peut-on requêter directement la base depuis Python ?

Oui, le fichier analytics.duckdb s’ouvre avec duckdb.connect("analytics.duckdb") et expose toutes les vues et tables construites par dbt. C’est ce qui rend l’intégration avec un script Python ou un dashboard Streamlit triviale.

Sponsoriser ce contenu

Cet emplacement est à vous

Position premium en fin d'article — c'est l'instant où les lecteurs sont le plus engagés. Réservez cet espace pour votre marque, votre formation ou votre offre.

Recevoir nos tarifs
Publicité