Dans l'article précédent, on a construit trois modèles avec deux en staging et un mart. Et si on va voir dans Snowsight, on remarque un truc et que ce sont tous des vues, pas des tables. On n'a rien demandé de tel, c'est dbt qui a choisi par défaut.
Pourquoi des vues ? Est-ce le bon choix ? Pas toujours. C'est exactement ce qu'on regarde aujourd'hui avec la matérialisation, c'est-à-dire la façon dont dbt transforme un SELECT en objet réel dans Snowflake. Quatre options, un seul mot-clé pour passer de l'une à l'autre.
C'est quoi une matérialisation
Quand on écrit un modèle dbt, on écrit juste un SELECT. La matérialisation, c'est la stratégie que dbt utilise pour stocker le résultat de ce SELECT dans Snowflake. Une vue ? Une table ? Quelque chose qui ne se reconstruit qu'en partie ?
dbt propose quatre matérialisations : view, table, incremental et ephemeral. Par défaut, c'est view. C'est pour ça que les trois modèles qu'on a créés sont des vues.
View : le choix par défaut
Une vue, c'est une requête enregistrée. Elle ne stocke aucune donnée : à chaque fois qu'on l'interroge, Snowflake rejoue le SELECT derrière.
Les avantages :
- Construction quasi instantanée au
dbt run(dbt crée juste unCREATE VIEW) - Aucun stockage consommé
- Donnée toujours à jour, puisqu'elle est recalculée à la lecture
La limite et que si la requête est lourde (grosses jointures, agrégats sur des millions de lignes), elle est recalculée à chaque interrogation. Donc un dashboard qui tape dessus 200 fois par jour, c'est 200 fois le même calcul. Là, la vue devient un mauvais plan.
Table : matérialiser pour aller vite à la lecture
Une table, c'est le résultat du SELECT figé physiquement dans Snowflake. dbt fait un CREATE TABLE AS SELECT et reconstruit la table entière à chaque dbt run.
Les avantages :
- Lecture rapide, le calcul a déjà été fait une fois
- Parfait pour les modèles métier interrogés souvent (les marts, ceux que l'outil BI consomme)
Le coût est du stockage, et surtout un recalcul complet à chaque dbt run. Si une table fait 500 millions de lignes et qu'on la reconstruit en entier tous les jours, la facture compute s'en souvient. Pour comprendre ce que coûte vraiment un recalcul, jette un oeil à Query Profile.
Comment changer la matérialisation
C'est là qu'arrive le fameux mot-clé. On ajoute un bloc config en haut du modèle. Ouvre ton mart mart_clients_commandes.sql et ajoute la première ligne :
{{ config(materialized='table') }}
with clients as (
select * from {{ ref('stg_customers') }}
),
commandes as (
select * from {{ ref('stg_orders') }}
)
select
c.customer_id,
c.prenom,
c.nom,
count(o.order_id) as nb_commandes,
sum(o.montant) as total_depense
from clients c
left join commandes o
on c.customer_id = o.customer_id
group by 1, 2, 3
dbt run
Retourne dans Snowsight, déplie ANALYTICS > DEV. Le mart_clients_commandes n'est plus une vue, c'est une table. Un seul mot-clé a tout changé, et on n'a pas touché au SQL.

Ephemeral : du code réutilisable, sans objet en base
La troisième option est plus discrète. Un modèle ephemeral n'est pas matérialisé dans Snowflake. dbt ne crée ni vue ni table. À la place, il injecte le SELECT directement comme une CTE dans les modèles qui l'appellent avec ref().
{{ config(materialized='ephemeral') }}
select
customer_id,
sum(montant) as total_depense
from {{ ref('stg_orders') }}
group by 1
Quand un autre modèle fait ref() sur celui-ci, dbt colle le code dedans au lieu de lire une table. C'est utile pour une logique intermédiaire courte, réutilisée à plusieurs endroits, mais qu'on ne veut pas exposer comme objet en base.
Incremental : ne reconstruire que le nouveau
La quatrième est la plus puissante et la plus technique. Un modèle incremental construit la table une première fois, puis aux exécutions suivantes ne traite que les nouvelles lignes au lieu de tout reconstruire.
Pourquoi recalculer 2 ans d'historique chaque nuit alors que seules les données d'hier ont changé ? On gagne un temps et un coût de calcul énormes.
C'est aussi le sujet le plus dense, avec la fonction is_incremental(), la clé unique_key et les stratégies de mise à jour. On lui consacrera un article entier plus loin dans la série. Pour l'instant, retiens juste qu'elle existe et à quoi elle sert.
Bonus Snowflake : la Dynamic Table
Les quatre matérialisations au-dessus existent sur tous les warehouses. Mais comme on est sur Snowflake on a accès à une cinquième, native et c'est la Dynamic Table.
L'idée est au lieu que dbt gère lui-même le delta (comme incremental), on déclare juste le SELECT et la fraîcheur cible, et Snowflake s'occupe du refresh incrémental tout seul.
Depuis dbt 1.12, c'est une matérialisation native. Tu l'actives comme les autres :
{{ config(
materialized='dynamic_table',
snowflake_warehouse='dbt_wh',
target_lag='1 hour'
) }}
select
customer_id,
sum(montant) as total_depense,
count(*) as nb_commandes
from {{ ref('stg_orders') }}
group by 1Deux réglages :
target_lag: la fraîcheur maximale qu'on accepte.'1 hour'veut dire "ne jamais avoir plus d'une heure de retard sur la source". Snowflake décide alors tout seul quand rafraîchir pour tenir cet objectif. On peut aussi mettre'downstream'pour qu'elle ne se rafraîchisse que quand une table en aval en a besoin.snowflake_warehouse: le warehouse qui exécute les refreshs.
Le gros intérêt est que si rien n'a changé en amont depuis le dernier refresh, Snowflake saute le refresh et donc le coût compute est nul.
dbt run --full-refresh (DROP/CREATE) et pour le refresh incrémental, les tables sources ont besoin d'avoir CHANGE_TRACKING = TRUE.Pour plus d'infos sur les dynamic table avec dbt voir la doc
Quand choisir quoi
| Matérialisation | Ce que dbt fait | Quand l'utiliser |
|---|---|---|
view |
Crée une vue, rien n'est stocké | Staging, modèles légers, donnée toujours fraîche |
table |
Reconstruit une table complète à chaque run | Marts lus souvent, requêtes lourdes |
ephemeral |
Injecte le SQL en CTE, aucun objet créé | Logique intermédiaire courte et réutilisée |
incremental |
Ne traite que les nouvelles lignes | Grosses tables d'événements ou de logs |
Le pattern recommandé : configurer par dossier
Mettre {{ config() }} dans chaque modèle, ça marche, mais ça devient vite répétitif. La bonne pratique, c'est de définir la matérialisation par dossier, une fois pour toutes, dans le dbt_project.yml.
Ouvre-le et configure la section models comme ça :
models:
dbt_projet:
staging:
+materialized: view
marts:
+materialized: table
On vient de dire que "tout ce qui est dans staging/ est une vue, tout ce qui est dans marts/ est une table". On peut maintenant retirer le {{ config(materialized='table') }} du mart, il est couvert par la règle du dossier.
Config au niveau modèle ou au niveau dossier ?
dbt_project.yml fixe le défaut pour tous les modèles du dossier. Le {{ config() }} dans un modèle précis prend le dessus. Donc en pratique : on définit la stratégie générale par dossier, et on ne met un config() dans un modèle que pour l'exception. C'est plus propre et ça se lit facilement.Cette logique colle au Médaillon qu'on a vu avec le staging (Silver), les marts (Gold) deviennent des tables rapides à lire pour la BI et dataviz. Et pour comprendre ce que ces tables deviennent côté Snowflake, j'ai détaillé les types de tables Snowflake dans la formation Snowflake.
La suite ?
Récap. de ce qu'on a vu dans cet article :
- Comprendre ce qu'est une matérialisation
- Voir les quatre stratégies :
view,table,ephemeral,incremental - Changer un modèle de vue en table avec un seul mot-clé
- Mettre en place la config par dossier dans
dbt_project.yml
Les modèles sont maintenant matérialisés intelligemment selon leur rôle. Dans le prochain article, on s'assure qu'ils sont aussi fiables donc on attaque les tests dbt, pour vérifier automatiquement que la donnée n'est pas cassée (clés en double, valeurs nulles, références orphelines etc....)
Aller plus loin
Cet article fait partie de la formation dbt complète, du premier modèle au déploiement en production.
👉 Suivre toute la formation dbt
dbt tourne sur Snowflake dans ce parcours. Pour maîtriser le socle (warehouses, rôles, ingestion) :
👉 Accéder à la formation Snowflake
Et pour t'entraîner sur dbt en conditions d'examen, la certification dbt Analytics Engineering teste précisément les matérialisations et leurs cas d'usage.
👉 Préparer la certification dbt sur DataCertification.fr
Tu veux que je t'accompagne sur ton projet data (Snowflake, dbt, modélisation, industrialisation) ?
👉 Réserver un appel de 30 minutes
Questions fréquentes
Pourquoi les modèles dbt sont des vues par défaut ?
dbt crée juste une vue, sans stockage ni recalcul à la construction, et la donnée reste toujours à jour. C'est un bon défaut pour démarrer et pour les couches légères comme le staging. On passe à une table seulement quand la lecture devient trop lente.
Vue ou table en dbt, comment choisir ?
Une vue ne stocke rien et recalcule à chaque lecture donc idéale pour les modèles légers ou peu interrogés. Une table fige le résultat et se lit vite, mais se reconstruit en entier à chaque run et consomme du stockage.
C'est quoi une matérialisation ephemeral en dbt ?
Un modèle ephemeral n'est pas matérialisé dans Snowflake. dbt injecte son SQL directement comme une CTE dans les modèles qui l'appellent avec ref(). C'est utile pour une logique intermédiaire courte et réutilisée, mais comme il n'existe pas en base, on ne peut ni l'interroger ni le tester directement.
Où configure-t-on la matérialisation d'un modèle dbt ?
À deux endroits. Dans le modèle lui-même avec un bloc config, par exemple config(materialized='table'). Ou par dossier dans le dbt_project.yml avec la clé +materialized, ce qui fixe le défaut pour tous les modèles du dossier. La config dans le modèle prend le dessus sur celle du dossier.
La matérialisation incremental, c'est pour quoi ?
Pour les grosses tables où reconstruire tout à chaque run serait trop coûteux. Un modèle incremental construit la table une fois, puis ne traite que les nouvelles lignes aux exécutions suivantes. C'est idéal pour les tables d'événements ou de logs, au prix d'une configuration un peu plus avancée avec is_incremental() et une clé unique.
