dbt (Outil de Construction de Données)
dbt gère la transformation des données dans l'entrepôt en utilisant SQL. La v2.0 introduit le Fusion Engine (Rust) pour les performances.
Quand l'utiliser
- Ingénierie Analytique et Transformations de Données : Transformer les données brutes dans les entrepôts cloud modernes (Snowflake, BigQuery, Databricks, Redshift).
- SQL Modulaire avec Jinja et Contrôle de Version : Construire des modèles SQL réutilisables avec des dépendances DAG et des macros.
- Tests de Qualité des Données et de Schéma : Appliquer des tests
unique,not_null, d'intégrité référentielle et des tests personnalisés singuliers. - Documentation Automatisée des Données et Traçabilité : Générer des graphes de dépendances interactifs directement à partir des schémas du code source.
Démarrage Rapide
-- models/marts/fct_orders.sql
{{ config(materialized='table') }}
with orders as (
select * from {{ ref('stg_orders') }}
),
payments as (
select * from {{ ref('stg_payments') }}
)
select
orders.order_id,
orders.customer_id,
orders.order_date,
coalesce(payments.amount, 0) as total_amount
from orders
left join payments using (order_id)
Concepts Fondamentaux
#Modélisation SQL Déclarative avec ref() & source()
Construction de modèles de transformation avec résolution des dépendances :
-- models/marts/core/fct_customer_orders.sql
{{
config(
materialized = 'incremental',
unique_key = 'order_id',
on_schema_change = 'fail'
)
}}
with orders as (
select * from {{ ref('stg_orders') }}
{% if is_incremental() %}
where order_date >= (select coalesce(max(order_date), '1970-01-01') from {{ this }})
{% endif %}
),
customers as (
select * from {{ ref('stg_customers') }}
)
select
o.order_id,
o.customer_id,
c.full_name as customer_name,
o.order_date,
o.total_amount_usd
from orders o
inner join customers c on o.customer_id = c.customer_id
#Tests de Schéma et Documentation en YAML
Application de contraintes de colonnes et documentation :
# models/marts/core/schema.yml
version: 2
models:
- name: fct_customer_orders
description: "Table de faits des commandes clients quotidienne combinant les commandes et les dimensions clients issues des couches staging."
columns:
- name: order_id
description: "Clé primaire pour les commandes."
tests:
- unique
- not_null
- name: customer_id
description: "Clé étrangère référençant les clients staging."
tests:
- not_null
- relationships:
to: ref('stg_customers')
field: customer_id
- name: total_amount_usd
description: "Valeur totale de la transaction en USD."
tests:
- not_null
#Macros Jinja pour une Logique Réutilisable DRY
Création d'utilitaires SQL réutilisables personnalisés :
-- macros/cents_to_dollars.sql
{% macro cents_to_dollars(column_name, decimal_places=2) -%}
round(cast(({{ column_name }} / 100.0) as numeric), {{ decimal_places }})
{%- endmacro %}
-- Utilisation dans le modèle :
select
id,
{{ cents_to_dollars('amount_in_cents') }} as amount_usd
from {{ ref('stg_payments') }}
Modèles Courants
Modèles Incrémentaux avec Filtrage par Timestamp
Problème : Reconstruire des tables de plusieurs milliards de lignes à partir de zéro à chaque exécution dbt est lent et coûteux.
Solution :
Utiliser la matérialisation incremental avec is_incremental() :
{{ config(
materialized='incremental',
unique_key='event_id'
) }}
select * from {{ source('raw', 'events') }}
{% if is_incremental() %}
-- Ne récupérer que les lignes créées après l'événement le plus récent dans la table cible
where event_timestamp > (select max(event_timestamp) from {{ this }})
{% endif %}
Meilleures Pratiques (2026)
- À faire : Organiser les projets en couches standard :
staging(1 pour 1 avec les sources brutes),intermediateetmarts. - À faire : Toujours utiliser
{{ ref('model_name') }}et{{ source('source_name', 'table_name') }}pour maintenir la traçabilité du DAG. - À faire : Implémenter des modèles incrémentaux (
materialized='incremental') pour les tables de faits de plusieurs millions de lignes. - À faire : Exécuter
dbt testdans les pipelines CI à chaque demande de tirage (pull request) pour détecter les régressions de schéma de données. - À ne pas faire : Écrire des références directes à des tables de base de données (par exemple
analytics.raw.users) ; utiliser toujourssource()ouref(). - À ne pas faire : Effectuer de la logique métier dans les modèles staging ; le staging doit seulement nettoyer, caster et renommer les colonnes.
- À ne pas faire : Coder en dur les environnements (dev vs prod) ; utiliser les conditionnels
target.namedans profiles.yml.
Dépannage
| Erreur | Cause | Solution |
|---|---|---|
Compilation Error: Model '...' depends on a node named '...' which was not found | Faute de frappe dans {{ ref('model_name') }} ou fichier de modèle manquant. | Vérifier que le nom du fichier correspond exactement à la chaîne référencée. |
dbt test failed: unique constraint violated | Clés en double générées par une condition de jointure non unique. | Inspecter les clés en double dans le SQL compilé dans target/compiled/. |
Schema change error during incremental run | Un modèle en amont a ajouté/supprimé des colonnes, cassant la table incrémentale existante. | Exécuter avec --full-refresh une fois pour reconstruire le schéma : dbt run --full-refresh. |