Avant d’écrire des macros dispatch personnalisées pour les différences SQL cross-database, vérifier si dbt fournit déjà ce dont on a besoin. Depuis la version 1.8, les fonctions cross-database les plus courantes vivent dans le namespace dbt dans le cadre de dbt-core lui-même. Pas de packages supplémentaires requis.
Le tableau de référence
| Macro | Objectif | Exemple |
|---|---|---|
dbt.dateadd() | Ajouter des intervalles aux dates | {{ dbt.dateadd("day", 7, "order_date") }} |
dbt.datediff() | Différence entre des dates | {{ dbt.datediff("start_date", "end_date", "day") }} |
dbt.date_trunc() | Tronquer à une précision | {{ dbt.date_trunc("month", "created_at") }} |
dbt.concat() | Concaténer des chaînes | {{ dbt.concat(["first_name", "' '", "last_name"]) }} |
dbt.safe_cast() | Transtyper avec NULL en cas d’échec | {{ dbt.safe_cast("col", api.Column.translate_type("string")) }} |
dbt.type_string() | Type chaîne de l’adaptateur | CAST(x AS {{ dbt.type_string() }}) |
dbt.type_timestamp() | Type timestamp de l’adaptateur | CAST(x AS {{ dbt.type_timestamp() }}) |
dbt.type_int() | Type entier de l’adaptateur | CAST(x AS {{ dbt.type_int() }}) |
Ces macros gèrent automatiquement la réorganisation des arguments, les différences de noms de fonctions, et la traduction des types. Sur BigQuery, dbt.dateadd("day", 7, "order_date") compile en DATE_ADD(order_date, INTERVAL 7 DAY). Sur Snowflake, le même appel produit DATEADD('day', 7, order_date).
Fonctions de date
Les fonctions de date couvrent le point de douleur cross-database le plus courant. La macro dbt.dateadd() prend trois arguments dans un ordre cohérent quel que soit le moteur cible :
{{ dbt.dateadd(datepart="day", interval=7, from_date_or_timestamp="order_date") }}Pour les différences de dates :
{{ dbt.datediff("start_date", "end_date", "day") }}Troncature de date :
{{ dbt.date_trunc("month", "created_at") }}Ces macros gèrent automatiquement les différences d’ordre des arguments entre BigQuery, Snowflake et Databricks. Pour une logique de date personnalisée au-delà de ce que les fonctions intégrées fournissent (trimestres fiscaux, calculs de jours ouvrés), utiliser le pattern dispatch avec des implémentations spécifiques à l’adaptateur :
{% macro get_fiscal_quarter_start(date_column) %} {{ return(adapter.dispatch('get_fiscal_quarter_start')(date_column)) }}{% endmacro %}
{% macro default__get_fiscal_quarter_start(date_column) %} date_trunc('quarter', {{ date_column }}){% endmacro %}
{% macro bigquery__get_fiscal_quarter_start(date_column) %} date_trunc({{ date_column }}, quarter){% endmacro %}Concaténation de chaînes
dbt.concat() gère les différences entre les bases de données qui utilisent la fonction CONCAT(), celles qui préfèrent l’opérateur ||, et celles qui ont des limites sur le nombre d’arguments :
{{ dbt.concat(["first_name", "' '", "last_name"]) }} AS full_nameC’est particulièrement utile car certaines bases de données limitent CONCAT() à deux arguments tandis que d’autres en acceptent beaucoup. La macro le gère de manière cohérente.
Transtypage sécurisé et helpers de types
Le transtypage sécurisé retourne NULL au lieu d’une erreur quand une conversion échoue. Le nom de la fonction diffère entre les bases de données (SAFE_CAST sur BigQuery, TRY_CAST ailleurs), mais dbt.safe_cast() abstrait cela :
{{ dbt.safe_cast("user_input", api.Column.translate_type("string")) }}La fonction api.Column.translate_type() convertit les noms de types génériques en équivalents spécifiques à l’adaptateur. "string" devient STRING sur BigQuery et VARCHAR sur Snowflake. Combiner avec safe_cast pour des conversions de types entièrement portables.
Pour un accès rapide aux types les plus courants sans translate_type() :
-- Ces valeurs retournent le nom de type correct pour l'adaptateurCAST({{ column }} AS {{ dbt.type_string() }}) -- STRING ou VARCHARCAST({{ column }} AS {{ dbt.type_int() }}) -- INT64 ou INTEGERCAST({{ column }} AS {{ dbt.type_timestamp() }}) -- TIMESTAMPUtiliser les helpers de types chaque fois que du DDL est généré ou que du SQL type-aware est écrit dans des macros. Hardcoder STRING fonctionne sur BigQuery mais échoue sur Snowflake.
Migration depuis dbt_utils
Beaucoup de ces macros vivaient auparavant dans le package dbt_utils. Depuis dbt-utils v1.0 (aligné avec dbt-core 1.8), elles ont déménagé vers le namespace dbt dans core. La migration est simple :
| Ancienne version (dépréciée) | Nouvelle version |
|---|---|
dbt_utils.dateadd() | dbt.dateadd() |
dbt_utils.datediff() | dbt.datediff() |
dbt_utils.date_trunc() | dbt.date_trunc() |
dbt_utils.concat() | dbt.concat() |
dbt_utils.safe_cast() | dbt.safe_cast() |
dbt_utils.type_string() | dbt.type_string() |
Les versions dbt_utils fonctionnent encore mais sont dépréciées et seront éventuellement supprimées. Pour la mise à niveau d’un projet plus ancien, un remplacement global find-and-replace de dbt_utils.dateadd vers dbt.dateadd (et similaire pour chaque fonction) gère la migration. Pas de changements d’arguments nécessaires — les signatures sont identiques.
Le déplacement vers core signifie que ces macros sont maintenant disponibles sans ajouter dbt_utils au packages.yml. Pour les nouveaux projets, dbt_utils peut ne pas être nécessaire du tout si son seul usage était pour les fonctions cross-database. Vérifier ce qui d’autre est importé depuis lui — generate_surrogate_key, star, get_column_values et date_spine vivent toujours dans dbt_utils.
Ce que les fonctions intégrées ne couvrent pas
Le namespace dbt gère les dates, les chaînes, les types et le transtypage. Il ne couvre pas :
- Opérations sur les tableaux —
UNNESTvsLATERAL FLATTENvsEXPLODErequiert des macros dispatch personnalisées. - Parsing JSON — Fonctions et syntaxe différentes entre les bases de données.
- Optimisations spécifiques à la base de données — Hints de clustering BigQuery,
RESULT_SCANSnowflake,OPTIMIZEDatabricks. - Agrégations approximatives —
APPROX_COUNT_DISTINCTet fonctions similaires varient en disponibilité et comportement.
Pour ces cas, écrire ses propres macros dispatch en suivant les patterns dans la note Macros dbt, et configurer l’ordre de recherche dispatch si on construit pour un usage multi-entrepôt.