Macros SQL cross-DB
Les macros Noria sont des fonctions Jinja2 qui génèrent du SQL adapté au dialecte cible. Écrire {{ cast('col', 'VARCHAR') }} produit CAST(col AS VARCHAR) en DuckDB et col::VARCHAR en PostgreSQL — sans changer le code SQL.
Macros built-in
cast(col, type) — conversion de type
| Dialecte | Résultat |
|---|---|
| DuckDB | CAST(montant AS DECIMAL(18,2)) |
| PostgreSQL | montant::DECIMAL(18,2) |
| BigQuery | CAST(montant AS DECIMAL) |
date_trunc(period, col) — tronquer une date
{{ date_trunc('month', 'date_commande') }} -- premier jour du mois
{{ date_trunc('year', 'date_commande') }} -- premier jour de l'année
{{ date_trunc('week', 'date_commande') }} -- premier jour de la semaine
{{ date_trunc('day', 'ts') }} -- minuit
{{ date_trunc('hour', 'ts') }}
Périodes disponibles : second, minute, hour, day, week, month, quarter, year.
| Dialecte | Résultat |
|---|---|
| DuckDB | DATE_TRUNC('month', date_commande) |
| PostgreSQL | DATE_TRUNC('month', date_commande) |
| BigQuery | DATE_TRUNC(date_commande, MONTH) |
current_timestamp() — timestamp courant
| Dialecte | Résultat |
|---|---|
| DuckDB | CURRENT_TIMESTAMP |
| PostgreSQL | NOW() |
| BigQuery | CURRENT_TIMESTAMP() |
concat(*args) — concaténation
| Dialecte | Résultat |
|---|---|
| DuckDB | CONCAT(prenom, ' ', nom) |
| PostgreSQL | CONCAT(prenom, ' ', nom) |
| BigQuery | CONCAT(prenom, ' ', nom) |
coalesce(*args) — première valeur non NULL
Tous les dialectes : COALESCE(email_pro, email_perso, 'inconnu')
if_null(col, default) — valeur par défaut si NULL
Tous les dialectes : COALESCE(commentaire, 'aucun')
safe_divide(numerator, denominator) — division sans risque de division par zéro
{{ safe_divide('ca_total', 'nb_commandes') }} AS panier_moyen
ROUND({{ safe_divide('nb_livres', 'nb_total') }} * 100, 1) AS taux_livraison
Tous les dialectes : CASE WHEN nb_commandes = 0 THEN NULL ELSE ca_total / nb_commandes END
to_char(col, format) — formater une date en texte
{{ to_char(date_trunc('month', 'date_op'), 'YYYY-MM') }} AS mois_label
{{ to_char('date_naissance', 'DD/MM/YYYY') }} AS date_fr
| Dialecte | Résultat |
|---|---|
| DuckDB | STRFTIME(col, 'YYYY-MM') |
| PostgreSQL | TO_CHAR(col, 'YYYY-MM') |
| BigQuery | FORMAT_TIMESTAMP('YYYY-MM', col) |
limit(n) — limiter le nombre de lignes
Tous les dialectes : LIMIT 100
schema_table(schema, table) — référence qualifiée
Résultat : main.clients (ou juste clients si schema est None)
Combiner les macros
Les macros se combinent naturellement :
SELECT
id_client,
{{ cast('id_client', 'VARCHAR') }} AS id_str,
{{ to_char(date_trunc('month', 'date_inscription'), 'YYYY-MM') }} AS mois_inscription,
{{ coalesce('email_pro', 'email_perso', "'inconnu'") }} AS email,
ROUND({{ safe_divide('ca_total', 'nb_commandes') }}, 2) AS panier_moyen,
{{ current_timestamp() }} AS updated_at
FROM {{ params.schema }}.clients
WHERE date_inscription >= {{ date_trunc('year', 'CURRENT_DATE') }}
Macros utilisateur
Crée des macros métier réutilisables dans sql/macros/ (fichiers .sql ou .sql.j2). Elles sont auto-chargées — aucun import nécessaire.
-- sql/macros/metier.sql.j2
{# Catégorise un montant de commande #}
{% macro categorie_panier(col) %}
CASE
WHEN {{ col }} < 50 THEN 'Petit'
WHEN {{ col }} < 200 THEN 'Moyen'
ELSE 'Gros'
END
{% endmacro %}
{# Taux en pourcentage avec arrondi #}
{% macro taux_pct(num, denom, decimals=1) %}
ROUND({{ safe_divide(num, denom) }} * 100, {{ decimals }})
{% endmacro %}
Utilisation dans n'importe quel SQL du projet :
SELECT
id_commande,
montant,
{{ categorie_panier('montant') }} AS categorie,
{{ taux_pct('nb_livres', 'nb_commandes') }} AS taux_livraison
FROM {{ params.schema }}.commandes
Surcharger une macro built-in
Si tu définis une macro utilisateur avec le même nom qu'une macro built-in, ta version a la priorité :
-- sql/macros/override.sql
{% macro current_timestamp() %}GETDATE(){% endmacro %}
-- Maintenant {{ current_timestamp() }} → GETDATE() partout dans ce projet