Setup dbt Core: Struttura Progetto, Profili di Connessione e Prima Trasformazione SQL
Dalla riga di comando alla prima trasformazione: configura dbt Core con profiles.yml per BigQuery, Snowflake o PostgreSQL, esplora la struttura del progetto (models, tests, seeds, macros) e scopri il macro ref() che crea dipendenze automatiche tra modelli.
Prerequisiti e Installazione
dbt Core richiede Python 3.8+ e si installa via pip. La cosa importante è installare non solo il pacchetto base ma anche l'adapter specifico per il tuo warehouse. Ogni warehouse ha il suo adapter, perché i dialetti SQL differiscono.
# Installa dbt Core con l'adapter per PostgreSQL (ottimo per iniziare in locale)
pip install dbt-postgres
# Per BigQuery
pip install dbt-bigquery
# Per Snowflake
pip install dbt-snowflake
# Per DuckDB (ideale per sviluppo locale senza infrastruttura)
pip install dbt-duckdb
# Verifica l'installazione
dbt --version
# Core:
# - installed: 1.9.0
# - latest: 1.9.0 - Up to date!
Inizia con DuckDB per lo Sviluppo Locale
Se non hai accesso immediato a BigQuery o Snowflake, DuckDB è il modo più rapido per iniziare: è un database embedded che gira direttamente in locale, non richiede infrastruttura e dbt-duckdb funziona su qualsiasi macchina. Perfetto per imparare senza costi.
Creare il Primo Progetto dbt
Il comando dbt init crea la struttura di cartelle di un progetto dbt e ti guida
nella configurazione iniziale:
dbt init jaffle_shop
# dbt chiederà:
# 1. Quale database vuoi usare? (postgres/bigquery/snowflake/...)
# 2. [Per postgres] host, port, user, password, database, schema
# La struttura creata:
jaffle_shop/
├── dbt_project.yml # configurazione principale
├── README.md
├── analyses/ # query ad hoc (non materializzate)
├── macros/ # funzioni Jinja riutilizzabili
├── models/
│ └── example/ # modelli di esempio (da eliminare)
├── seeds/ # CSV statici
├── snapshots/ # snapshot per SCD
└── tests/ # test SQL singolari
Il File dbt_project.yml
Il cuore della configurazione del progetto è dbt_project.yml. Defini qui il
nome del progetto, la versione di dbt, i percorsi delle directory e la configurazione globale
dei modelli:
# dbt_project.yml
name: 'jaffle_shop'
version: '1.0.0'
# Versione minima di dbt richiesta
require-dbt-version: ">=1.8.0"
# Percorso del profile da usare (in profiles.yml)
profile: 'jaffle_shop'
# Directory dei modelli
model-paths: ["models"]
analysis-paths: ["analyses"]
test-paths: ["tests"]
seed-paths: ["seeds"]
macro-paths: ["macros"]
snapshot-paths: ["snapshots"]
# Dove salvare i log e i target compilati
target-path: "target"
log-path: "logs"
clean-targets: ["target", "dbt_packages"]
# Configurazione dei modelli per directory
models:
jaffle_shop:
# Tutti i modelli del progetto sono view per default
+materialized: view
staging:
# I modelli in staging/ sono sempre view
+materialized: view
+schema: staging # savedano in schema 'staging'
marts:
+materialized: table # I marts sono table per performance
+schema: marts
Il File profiles.yml
Le credenziali di connessione vanno in ~/.dbt/profiles.yml (nella home directory,
mai nel repository). Ogni profilo può avere più target (dev, staging, prod):
# ~/.dbt/profiles.yml
jaffle_shop:
target: dev # target di default
outputs:
dev:
type: postgres
host: localhost
port: 5432
user: "{{ env_var('DB_USER') }}" # usa variabili d'ambiente
password: "{{ env_var('DB_PASSWORD') }}"
dbname: jaffle_shop_dev
schema: dbt_dev_federico # schema personale per sviluppo
threads: 4
prod:
type: postgres
host: "{{ env_var('PROD_DB_HOST') }}"
port: 5432
user: "{{ env_var('PROD_DB_USER') }}"
password: "{{ env_var('PROD_DB_PASSWORD') }}"
dbname: jaffle_shop_prod
schema: dbt_prod
threads: 8
La convenzione è che ogni sviluppatore abbia uno schema personale (es. dbt_dev_federico)
per evitare conflitti durante lo sviluppo in parallelo.
Modelli dbt: La Unit Fondamentale
Un modello dbt è un file .sql nella directory models/. Il contenuto
è un semplice SELECT - dbt si occupa di creare la view o la table corrispondente nel warehouse.
Iniziamo con un esempio realistico. Supponiamo di avere una tabella raw.orders nel
warehouse con dati grezzi provenienti dall'applicazione:
-- models/staging/stg_orders.sql
-- Staging model: rinomina, casta, pulisce - nessuna logica di business
WITH source AS (
SELECT * FROM {{ source('raw', 'orders') }} -- 'source' punta alla sorgente raw
),
renamed AS (
SELECT
id AS order_id,
user_id AS customer_id,
order_date AS created_at,
status,
CAST(amount AS DECIMAL(10,2)) AS total_amount,
LOWER(payment_method) AS payment_method,
_loaded_at AS loaded_at -- metadata pipeline
FROM source
WHERE id IS NOT NULL -- filtra record corrotti
)
SELECT * FROM renamed
Il Macro ref()
Il macro ref() è la feature più importante di dbt. Quando scrivi
{{ ref('stg_orders') }}, dbt:
- Risolve il nome corretto della tabella/view nel warehouse per l'ambiente corrente
- Registra la dipendenza nel DAG (directed acyclic graph)
- Garantisce che il modello dipendente venga eseguito prima
-- models/marts/finance/orders_monthly.sql
-- Usa ref() per dipendere da stg_orders
WITH orders AS (
SELECT * FROM {{ ref('stg_orders') }} -- dbt risolve automaticamente lo schema
),
monthly_aggregated AS (
SELECT
DATE_TRUNC('month', created_at) AS month,
payment_method,
COUNT(*) AS order_count,
SUM(total_amount) AS gross_revenue,
AVG(total_amount) AS avg_order_value,
COUNT(DISTINCT customer_id) AS unique_customers
FROM orders
WHERE status = 'completed'
GROUP BY 1, 2
)
SELECT * FROM monthly_aggregated
Il Macro source()
Per accedere alle sorgenti raw (tabelle non create da dbt), usa source() invece di
ref(). Le sorgenti vanno dichiarate in un file sources.yml:
# models/staging/sources.yml
version: 2
sources:
- name: raw # nome del source group
database: raw_db # database nel warehouse
schema: public # schema nel warehouse
tables:
- name: orders
description: "Ordini grezzi dall'applicazione"
loaded_at_field: _loaded_at # campo per freshness check
freshness:
warn_after: {count: 12, period: hour}
error_after: {count: 24, period: hour}
- name: customers
description: "Clienti grezzi dall'applicazione"
Con questa configurazione, puoi eseguire dbt source freshness per verificare che
le sorgenti siano aggiornate prima di avviare le trasformazioni.
Eseguire dbt: i Comandi Fondamentali
# Esegui tutti i modelli (materialization nel warehouse)
dbt run
# Esegui solo i modelli staging
dbt run --select staging
# Esegui un singolo modello e tutte le sue dipendenze (+)
dbt run --select +orders_monthly
# Esegui tutti i test definiti nello schema YAML
dbt test
# Testa solo un modello specifico
dbt test --select stg_orders
# Compila i modelli senza eseguirli (utile per debug)
dbt compile
# Verifica freshness delle sorgenti
dbt source freshness
# Genera e serve la documentazione
dbt docs generate
dbt docs serve # apre http://localhost:8080
Struttura Raccomandata dei Modelli
La struttura a tre livelli raccomandata dalla comunità dbt:
models/
├── staging/ # Layer 1: vicino alla sorgente
│ ├── sources.yml # dichiarazione sorgenti
│ ├── schema.yml # test + documentazione
│ ├── stg_orders.sql
│ ├── stg_customers.sql
│ └── stg_products.sql
├── intermediate/ # Layer 2: join complessi (opzionale)
│ ├── int_orders_enriched.sql # join ordini + clienti
└── marts/ # Layer 3: pronti per consumo
├── finance/
│ ├── schema.yml
│ ├── orders_monthly.sql
│ └── revenue_by_country.sql
└── marketing/
└── customer_cohorts.sql
Anti-pattern: Logica di Business nello Staging
I modelli di staging devono fare solo operazioni "stupide": rinominare colonne, castare tipi, deduplicare. La logica di business (calcoli, aggregazioni, join) va nei modelli intermediate o marts. Se il tuo staging model ha un GROUP BY o più di un paio di colonne calcolate, probabilmente stai facendo troppo in quel layer.
Verifica del Setup con dbt debug
Prima di eseguire dbt run, verifica che la connessione al warehouse funzioni:
dbt debug
# Output atteso:
# Configuration:
# profiles.yml file [OK found and valid]
# dbt_project.yml file [OK found and valid]
# Required dependencies:
# - git [OK found]
# Connection:
# host: localhost
# port: 5432
# user: federico
# database: jaffle_shop_dev
# schema: dbt_dev_federico
# [OK connection ok]
Conclusioni e Prossimi Passi
Abbiamo configurato un progetto dbt funzionante con la struttura a tre livelli (staging →
intermediate → marts), i profili di connessione per diversi ambienti, le sorgenti dichiarate
con freshness check e i primi modelli con ref() e source().
Il passo successivo è rendere il nostro SQL dinamico con Jinja: variabili, loop, condizioni e macro riutilizzabili che eliminano la duplicazione di codice nel warehouse.

Commenti
Caricamento commenti...
Accedi per commentare