Warunki wstępne i instalacja

dbt Core wymaga Pythona 3.8 lub nowszego i instaluje się przez pip. Najważniejszą rzeczą jest zainstalowanie nie tylko pakiet podstawowy, ale takżekonkretnego adaptera dla Twojego magazynu. Każdy magazyn ma swój własny adapter, ponieważ dialekty SQL są różne.

# 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!

Zacznij korzystać z DuckDB dla rozwoju lokalnego

Jeśli nie masz bezpośredniego dostępu do BigQuery lub Snowflake, DuckDB to jest sposób szybszy start: jest to wbudowana baza danych, która działa bezpośrednio lokalnie, nie wymaga żadnej infrastruktury, a dbt-duckdb działa na każdym komputerze. Idealny do nauki bez kosztów.

Utwórz pierwszy projekt dbt

Polecenie dbt init tworzy strukturę folderów projektu dbt i prowadzi Cię w konfiguracji początkowej:

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

Plik dbt_project.yml

Sercem konfiguracji projektu jest dbt_project.yml. Zdefiniuj tutaj nazwa projektu, wersja dbt, ścieżki katalogów i konfiguracja globalna z modeli:

# 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

Plik profiles.yml

Poświadczenia połączenia zostaną wprowadzone ~/.dbt/profiles.yml (w katalogu domowym, nigdy w repozytorium). Każdy profil może mieć wiele celów (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

Konwencja jest taka, że ​​każdy programista ma swój osobisty wzorzec (np. dbt_dev_federico) aby uniknąć konfliktów podczas rozwoju równoległego.

modele dbt: Jednostka podstawowa

Model dbt jest plikiem .sql w katalogu models/. Treść to prosty SELECT — dbt zajmuje się utworzeniem odpowiedniego widoku lub tabeli w magazynie.

Zacznijmy od realistycznego przykładu. Załóżmy, że mamy stół raw.orders w hurtownia z surowymi danymi z aplikacji:

-- 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

Makro ref().

Makro ref() jest to najważniejsza cecha dbt. Kiedy piszesz {{ ref('stg_orders') }}, dbt:

  1. Rozwiązuje poprawną nazwę tabeli/widoku w magazynie dla bieżącego środowiska
  2. Zapisz zależność na skierowanym grafie acyklicznym (DAG)
  3. Zapewnia, że ​​model zależny działa jako pierwszy
-- 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

Makro źródłowe ().

Aby uzyskać dostęp do surowych źródeł (tabele nie utworzone przez dbt), użyj source() zamiast ref(). Źródła muszą być zadeklarowane w pliku 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"

Dzięki tej konfiguracji możesz biegać dbt source freshness aby to zweryfikować źródła są aktualizowane przed rozpoczęciem transformacji.

Uruchom dbt: podstawowe polecenia

# 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

Zalecana struktura szablonu

Trójpoziomowa struktura zalecana przez społeczność 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

Antywzorzec: logika biznesowa w inscenizacji

Szablony przemieszczania muszą wykonywać tylko „głupie” operacje: zmieniać nazwy kolumn, typy rzutowania, deduplikować. Logika biznesowa (obliczenia, agregacje, łączenia) przechodzi do modeli pośrednich lub marty. Jeśli Twój model pomostowy ma GROUP BY lub więcej niż kilka kolumn obliczeniowych, prawdopodobnie robisz za dużo w tej warstwie.

Sprawdzanie konfiguracji za pomocą debugowania dbt

Przed biegiem dbt run, sprawdź, czy połączenie z magazynem działa:

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]

Wnioski i dalsze kroki

Skonfigurowaliśmy działający projekt dbt o trzypoziomowej strukturze (staging → średniozaawansowany → mart), profile połączeń dla różnych środowisk, deklarowane źródła z kontrolą świeżości i pierwszymi modelami z ref() e source().

Następnym krokiem jest zdynamizowanie naszego SQL za pomocą Jinja: zmienne, pętle, warunki i makra wielokrotnego użytku, które eliminują powielanie kodu w magazynie.