Comportements spécifiques à chaque plateforme pour les stratégies incrémentielles dbt sur BigQuery, Snowflake et Databricks. Le même nom de stratégie produit un comportement différent selon les entrepôts — des différences suffisamment significatives pour causer des échecs silencieux ou des coûts inattendus lors du portage de modèles entre plateformes.
BigQuery
Particularités du merge
Le merge de BigQuery exige unique_key. Ce n’est pas simplement une bonne pratique — sans elle, le merge échoue entièrement. Sur Snowflake et Databricks, omettre unique_key avec merge produit un comportement d’ajout. Sur BigQuery, cela produit une erreur. Si vous portez des modèles depuis Snowflake, c’est l’une des premières choses qui casse.
BigQuery ne supporte pas le pruning dynamique des partitions dans les opérations de merge. Une sous-requête dans vos incremental_predicates ne déclenchera pas de pruning :
-- Ceci NE prune PAS sur BigQueryincremental_predicates=[ "DBT_INTERNAL_DEST.event_date = (SELECT MAX(event_date) FROM source_table)"]
-- Ceci pruneincremental_predicates=[ "DBT_INTERNAL_DEST.event_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)"]BigQuery a besoin de valeurs littérales ou d’arithmétique de dates déterministe au moment de la planification de la requête. Les sous-requêtes produisent des valeurs au moment de l’exécution, ce qui est trop tard pour le pruneur de partitions. C’est la même limitation qui affecte tout le pruning des partitions BigQuery — ce n’est pas spécifique au merge, mais c’est plus douloureux dans le contexte du merge car le scan de destination est la partie coûteuse.
L’option de table require_partition_filter=true interagit mal avec le merge. Si vous avez activé cette option sur votre table cible (une garde-fou de gouvernance des coûts courant), l’instruction merge échoue sauf si vous définissez également des incremental_predicates qui incluent un filtre de partition. L’opération MERGE ne sait pas intrinsèquement quelles partitions toucher, et BigQuery applique l’exigence de filtre de partition sur le scan de destination.
Points forts de Insert_Overwrite
Insert_overwrite est là où BigQuery excelle. C’est la stratégie la plus rentable pour les tables de faits partitionnées par le temps, et l’implémentation de BigQuery est la plus mature des trois entrepôts.
Deux modes sont disponibles :
Partitions dynamiques : BigQuery interroge la source pour découvrir quelles partitions contiennent de nouvelles données, puis remplace ces partitions de façon atomique. La requête de découverte ajoute de la latence mais gère les cas où vous ne savez pas à l’avance quelles partitions seront affectées.
Partitions statiques : Vous listez explicitement les partitions à remplacer. Plus rapide que les partitions dynamiques car BigQuery ignore entièrement la requête de découverte. La variable _dbt_max_partition qui apparaît dans certaines requêtes de partitions dynamiques est du scripting BigQuery, pas du Jinja — elle s’exécute au moment de la requête dans l’entrepôt, pas pendant la compilation dbt.
-- Partitions statiques : plus rapides, pas de surcharge de découverte{% set partitions_to_replace = [ 'CURRENT_DATE()', 'DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY)', 'DATE_SUB(CURRENT_DATE(), INTERVAL 2 DAY)'] %}
{{ config( materialized='incremental', incremental_strategy='insert_overwrite', partition_by={'field': 'event_date', 'data_type': 'date'}, partitions=partitions_to_replace) }}Les équipes passant de merge à insert_overwrite avec des partitions statiques rapportent des réductions de coûts de 100 à 200x sur les grandes tables : merge scanne la table de destination entière tandis qu’insert_overwrite ne touche que les partitions remplacées.
Ce que BigQuery ne supporte pas
BigQuery ne supporte pas les stratégies append ou delete+insert. Si vous avez besoin d’un comportement de type append, utilisez merge sans unique_key… ce qui ne fonctionne pas non plus sur BigQuery. Votre option réelle est d’utiliser un post-hook pour INSERT INTO sans déduplication, mais à ce stade vous combattez l’outil. Pour les patterns append-only sur BigQuery, insert_overwrite avec une liste de partitions est une approche plus propre.
Snowflake
Comportement du merge
Le merge de Snowflake suit la syntaxe standard MERGE INTO et fonctionne comme prévu — jusqu’à ce que vos données ne soient pas propres. L’erreur « nondeterministic merge » apparaît quand votre unique_key n’est pas vraiment unique dans les données source. Si deux lignes source correspondent à la même ligne de destination, Snowflake ne sait pas laquelle appliquer et produit une erreur plutôt que de choisir arbitrairement.
-- Cette erreur signifie que votre unique_key a des doublons dans la sourceSQL compilation error: Merge is nondeterministic:multiple source rows matched the same target rowLa correction consiste à pré-dédupliquer dans votre SELECT (voir les modèles incrémentiels idempotents). L’erreur est en réalité une fonctionnalité — elle expose un problème de qualité des données que BigQuery et Databricks ignorent silencieusement en appliquant la dernière ligne correspondante.
Les performances se dégradent sensiblement au-delà de 100 M de lignes. Le pruning des micro-partitions de Snowflake aide avec les prédicats incrémentiels, mais la comparaison ligne par ligne inhérente au merge reste le goulot d’étranglement à grande échelle.
Avantages de Delete+Insert
Delete+insert est la sortie de secours de Snowflake du merge à grande échelle. Les benchmarks montrent une exécution 3,4x plus rapide à plus de 500 M de lignes par rapport au merge. L’écart de performance s’élargit à mesure que les tables grandissent car delete+insert évite entièrement la comparaison ligne par ligne.
Un détail de configuration qui piège les gens : quand unique_key est défini, vous pourriez avoir besoin de tmp_relation_type: table pour éviter des problèmes de matérialisation :
{{ config( materialized='incremental', unique_key='order_id', incremental_strategy='delete+insert') }}Delete+insert n’est pas atomique. La suppression et l’insertion sont deux opérations séparées. Si le processus échoue entre les deux, vous avez des données manquantes jusqu’à la prochaine exécution réussie. Pour la plupart des workloads analytiques batch, cet état intermédiaire bref est acceptable. Pour tout ce qui alimente des décisions en temps réel, ce ne l’est pas.
Le piège Insert_Overwrite
C’est le piège le plus dangereux des trois entrepôts : insert_overwrite sur Snowflake remplace la table entière, pas les partitions individuelles. Le nom de la stratégie implique un remplacement au niveau des partitions (et c’est exactement ce qu’elle fait sur BigQuery et Databricks), mais l’implémentation de Snowflake est un remplacement complet de la table.
Si votre requête incrémentale sélectionne seulement les 3 derniers jours de données et que vous utilisez insert_overwrite sur Snowflake, vous n’obtenez pas une table avec les 3 derniers jours mis à jour — vous obtenez une table contenant uniquement les 3 derniers jours. Tout le reste a disparu.
Cette incohérence de nommage a causé des pertes de données pour de nombreuses équipes. Aucune configuration ne change ce comportement. Sur Snowflake, si vous avez besoin d’un remplacement de type partition, utilisez delete+insert avec des prédicats de date explicites dans votre clause WHERE.
Databricks
Merge sur Delta Lake
Le merge sur Databricks bénéficie des garanties ACID de Delta Lake, mais offre également le plus d’options de configuration des trois entrepôts. Au-delà des bases, Databricks supporte :
Évolution de schéma : merge_with_schema_evolution=true ajoute automatiquement les nouvelles colonnes de la source vers la cible lors des opérations de merge. C’est plus gracieux que les options on_schema_change de dbt car cela se produit au niveau Delta Lake plutôt que de nécessiter des instructions ALTER TABLE.
Patterns CDC avancés : matched_condition vous permet d’ajouter des filtres supplémentaires à la clause WHEN MATCHED, et not_matched_by_source_action='delete' permet la suppression automatique des enregistrements qui existent dans la cible mais pas dans la source. Cela rend la stratégie merge viable pour les pipelines CDC qui ont besoin de propager les suppressions.
{{ config( materialized='incremental', unique_key='customer_id', incremental_strategy='merge', merge_with_schema_evolution=true) }}Spécificités d’Insert_Overwrite
Databricks utilise le remplacement dynamique des partitions par défaut, ce qui fonctionne généralement correctement. Mais une régression dans la version 1.8.0 a changé le comportement en remplacement complet de la table au lieu du remplacement des partitions. Si vous êtes sur une version affectée par cette régression, définissez la configuration Spark explicitement :
spark.sql.sources.partitionOverwriteMode=DYNAMICIl s’agit d’un paramètre au niveau du cluster ou de la session, pas d’une config dbt. Si vous déboguez des pertes de données inattendues avec insert_overwrite sur Databricks, ce paramètre est la première chose à vérifier.
Replace_Where : l’option exclusive à Databricks
replace_where est souvent le meilleur choix pour les mises à jour bornées dans le temps sur Databricks, notamment sur les SQL Warehouses où le comportement des partitions insert_overwrite peut être moins prévisible. Il utilise la syntaxe Delta Lake INSERT INTO ... REPLACE WHERE :
{{ config( materialized='incremental', incremental_strategy='replace_where', incremental_predicates=["event_date >= CURRENT_DATE - INTERVAL 3 DAY"]) }}Le prédicat peut être n’importe quelle expression, pas seulement une frontière de partition. Cela rend replace_where plus flexible qu’insert_overwrite tout en restant atomique. Le compromis : il est exclusif à Databricks et nécessite Delta Lake, donc votre modèle n’est pas portable si vous devez supporter un autre entrepôt.
Mapping des stratégies entre entrepôts
Quand vous décidez quelle stratégie utiliser et que vous supportez plusieurs entrepôts, ce mapping montre les équivalents pratiques :
| Cas d’usage | BigQuery | Snowflake | Databricks |
|---|---|---|---|
| Mises à jour au niveau des lignes, petites tables | merge | merge | merge |
| Mises à jour au niveau des lignes, grandes tables | merge + prédicats | delete+insert | merge (Delta optimisé) |
| Remplacement de partitions | insert_overwrite | delete+insert (avec filtre de date) | insert_overwrite ou replace_where |
| Append uniquement | insert_overwrite (remplacer la partition du jour) | append | append |
La même intention mappe vers des stratégies différentes selon les entrepôts. « Remplacer les données d’aujourd’hui » est insert_overwrite sur BigQuery, delete+insert sur Snowflake, et insert_overwrite ou replace_where sur Databricks. Le cadre de décision s’applique par entrepôt, pas comme un choix universel.