← Terug naar Downloads DataPartner365 — datapartner365.nl
DataPartner365 · Handleiding · 2026

Data Modelleren met dbt

Van installatie tot productie: een complete handleiding voor het bouwen van betrouwbare, geteste en gedocumenteerde datamodellen met dbt (data build tool).

dbt SQL Jinja Databricks Snowflake Gratis
1

Wat is dbt & installatie

pip install dbt-core, dbt init project, adapter kiezen

dbt (data build tool) is een open-source tool waarmee data engineers SQL-modellen schrijven die dbt omzet naar geteste, gedocumenteerde tabellen en views in je datawarehouse. dbt handelt de T in ELT — het transformeren van data — af binnen het warehouse zelf.

Waarom dbt?

  • SQL-first: data engineers werken in een vertrouwde taal
  • Ingebouwde tests (unique, not_null, accepted_values, relationships)
  • Automatische documentatie met lineage graph
  • Modulaire modellen via ref() en source()
  • Werkt met Snowflake, BigQuery, Databricks, Redshift, DuckDB en meer

Adapter kiezen en installeren

PlatformPakketVersie
Snowflakedbt-snowflake≥ 1.7
Databricksdbt-databricks≥ 1.7
BigQuerydbt-bigquery≥ 1.7
Azure Synapsedbt-synapse≥ 1.7
Lokaal testendbt-duckdb≥ 1.7
bash — installatie
# Virtuele omgeving aanmaken (aanbevolen)
python -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate

# Installeer dbt met de gewenste adapter
pip install dbt-core dbt-snowflake       # voor Snowflake
pip install dbt-core dbt-databricks      # voor Databricks
pip install dbt-core dbt-duckdb          # voor lokaal testen

# Controleer installatie
dbt --version
# dbt Core: 1.8.x  |  Adapter: dbt-snowflake: 1.8.x

# Nieuw project aanmaken
dbt init mijn_project
# → kies je adapter en vul de verbindingsgegevens in

cd mijn_project
dbt debug    # test de verbinding met je datawarehouse
Tip: Gebruik dbt-duckdb voor lokaal ontwikkelen zonder cloud kosten. Switch naar de productie-adapter zodra je klaar bent voor deployment.
2

Projectstructuur

models/, seeds/, tests/, macros/, dbt_project.yml

Een dbt project heeft een vaste mappenstructuur. Begrijpen wat elke map doet is essentieel voor een schaalbaar project.

projectstructuur
mijn_project/
├── dbt_project.yml          # Projectconfiguratie (naam, versie, paths)
├── profiles.yml             # Verbindingsinstellingen (buiten repo!)
├── packages.yml             # dbt packages (dbt_utils, dbt_expectations)
│
├── models/                  # SQL-transformaties — de kern van dbt
│   ├── staging/             # 1-op-1 mapping van bronnen
│   │   └── stg_orders.sql
│   ├── intermediate/        # Tussenliggende logica
│   │   └── int_orders_enriched.sql
│   └── marts/               # Eindproducten voor BI tools
│       ├── core/
│       │   └── dim_customers.sql
│       └── finance/
│           └── fct_revenue.sql
│
├── seeds/                   # Kleine CSV bestanden → tabellen in DWH
│   └── country_codes.csv
│
├── tests/                   # Singular tests (custom SQL assertions)
│   └── assert_orders_positive.sql
│
├── macros/                  # Herbruikbare Jinja functies
│   └── cents_to_euros.sql
│
├── snapshots/               # SCD Type 2 snapshots
│   └── orders_snapshot.sql
│
└── analyses/                # Ad-hoc analyses (niet materialiseerd)

dbt_project.yml configureren

yaml — dbt_project.yml
name: 'mijn_project'
version: '1.0.0'
config-version: 2

profile: 'mijn_project'

model-paths: ["models"]
seed-paths:  ["seeds"]
test-paths:  ["tests"]
macro-paths: ["macros"]
snapshot-paths: ["snapshots"]

target-path: "target"
clean-targets: ["target", "dbt_packages"]

models:
  mijn_project:
    staging:
      +materialized: view       # stagings zijn views
      +schema: staging
    intermediate:
      +materialized: ephemeral  # geen tabel, inline CTE
    marts:
      +materialized: table      # eindproducten zijn tabellen
      core:
        +schema: core
      finance:
        +schema: finance
3

Je eerste model schrijven

SQL + Jinja, ref(), source(), config block

Een dbt model is een .sql bestand met een SELECT statement. dbt wikkelt dit automatisch in een CREATE TABLE of CREATE VIEW op basis van de materialisatie.

Eenvoudig model met ref()

sql — models/marts/core/fct_orders.sql
-- Config block bovenaan het bestand (optioneel, overschrijft dbt_project.yml)
{{
  config(
    materialized = 'table',
    schema       = 'core',
    tags         = ['daily', 'finance']
  )
}}

with orders as (

  select * from {{ ref('stg_orders') }}   -- verwijzing naar een ander model

),

customers as (

  select * from {{ ref('dim_customers') }}

),

final as (

  select
    o.order_id,
    o.order_date,
    o.status,
    o.amount_cents / 100.0 as amount_euros,
    c.customer_name,
    c.country
  from orders o
  left join customers c on o.customer_id = c.customer_id

)

select * from final

Jinja variabelen en if-statements

sql — jinja voorbeeld
-- Jinja variabelen (worden gecompileerd voor uitvoering) {% set payment_methods = ['ideal', 'creditcard', 'banktransfer'] %} select order_id, {% for method in payment_methods %} sum(case when payment_method = '{{ method }}' then amount else 0 end) as {{ method }}_amount {% if not loop.last %},{% endif %} {% endfor %} from {{ ref('stg_payments') }} group by 1
ℹ️ Gebruik altijd ref() om naar andere dbt modellen te verwijzen. dbt bouwt hieruit de dependency graph en voert modellen in de juiste volgorde uit.
4

Sources & staging modellen

Bronnen declareren in schema.yml, stg_ naamconventie

Sources zijn ruwe tabellen in je datawarehouse die door externe processen worden geladen (bijv. Fivetran, ADF, Airbyte). Je declareert ze in een schema.yml bestand.

yaml — models/staging/schema.yml
version: 2 sources: - name: raw_salesforce database: raw_db # database naam (Snowflake) schema: salesforce loaded_at_field: _loaded_at # voor freshness checks freshness: warn_after: {count: 6, period: hour} error_after: {count: 24, period: hour} tables: - name: orders description: "Ruwe orders uit Salesforce CRM" columns: - name: id description: "Primaire sleutel" tests: - unique - not_null - name: status tests: - accepted_values: values: ['open', 'closed', 'pending']

Staging model met source()

sql — models/staging/stg_orders.sql
{{ config(materialized='view') }} select -- Identifiers id as order_id, customer_id, -- Datums cast(created_at as timestamp) as order_created_at, cast(updated_at as timestamp) as order_updated_at, -- Bedragen amount_cents, currency, -- Status lower(status) as status, -- Metadata _loaded_at from {{ source('raw_salesforce', 'orders') }}
Best practice: Staging modellen doen alleen type casting en hernoemingen. Geen joins, geen business logica. Dat hoort in intermediate of marts.
5

Incrementele modellen

is_incremental(), unique_key, merge strategie

Incrementele modellen verwerken alleen nieuwe of gewijzigde records bij elke run. Dit spaart rekentijd bij grote tabellen.

sql — incrementeel model
{{ config( materialized = 'incremental', unique_key = 'order_id', # merge op basis van deze kolom on_schema_change = 'sync_all_columns' # kolommen automatisch bijwerken ) }} select order_id, customer_id, status, amount_cents, order_created_at, order_updated_at from {{ ref('stg_orders') }} {% if is_incremental() %} -- Dit blok wordt alleen uitgevoerd bij incrementele runs (niet bij eerste run) where order_updated_at > ( select max(order_updated_at) from {{ this }} ) {% endif %}

Volledige refresh forceren

bash
# Normaal: alleen nieuwe records dbt run --select fct_orders # Volledige rebuild (dropt en herbouwt de tabel) dbt run --select fct_orders --full-refresh
⚠️ Let op: Bij unique_key voert dbt een MERGE uit. Zonder unique_key wordt alleen INSERT gebruikt. Gebruik altijd een unieke sleutel voor correcte idempotentie.
6

SCD Type 2 implementeren

dbt snapshots voor historische data

Met dbt snapshots implementeer je Slowly Changing Dimension Type 2 — historische versies van records worden bewaard met dbt_valid_from en dbt_valid_to kolommen.

sql — snapshots/customers_snapshot.sql
{% snapshot customers_snapshot %} {{ config( target_schema = 'snapshots', unique_key = 'customer_id', strategy = 'timestamp', # of 'check' voor kolom-vergelijking updated_at = 'updated_at', invalidate_hard_deletes = true ) }} select * from {{ source('raw_salesforce', 'customers') }} {% endsnapshot %}

Snapshot uitvoeren

bash
# Alle snapshots uitvoeren dbt snapshot # Snapshot vervolgens in een mart gebruiken # In je model: select * from {{ ref('customers_snapshot') }} # Actieve records filteren: # where dbt_valid_to is null

Resulterende kolommen in snapshot tabel

KolomBeschrijving
dbt_scd_idUnieke hash per versie
dbt_updated_atTijdstip van de wijziging
dbt_valid_fromBegin van deze versie
dbt_valid_toEinde van deze versie (NULL = actief)
7

Tests toevoegen

Generic tests, singular tests, dbt_expectations

dbt heeft twee soorten tests: generic tests (gedeclareerd in YAML) en singular tests (custom SQL bestanden).

Generic tests in schema.yml

yaml — models/marts/core/schema.yml
version: 2 models: - name: fct_orders description: "Feitentabel met alle orders" columns: - name: order_id description: "Primaire sleutel" tests: - unique - not_null - name: status tests: - not_null - accepted_values: values: ['open', 'closed', 'pending', 'refunded'] - name: customer_id tests: - not_null - relationships: to: ref('dim_customers') field: customer_id

Singular test (custom SQL)

sql — tests/assert_orders_positive_amount.sql
-- Test faalt als deze query rijen teruggeeft select order_id, amount_cents from {{ ref('fct_orders') }} where amount_cents < 0

Tests uitvoeren

bash
# Alle tests dbt test # Tests voor één model dbt test --select fct_orders # Alleen generic tests dbt test --select test_type:generic # Build: run + test in één stap dbt build --select fct_orders+
Installeer dbt_expectations voor geavanceerde tests zoals expect_column_values_to_be_between, regex checks en statistisch testen. Voeg dbt-labs/dbt_expectations toe aan packages.yml.
8

Documentatie

schema.yml descriptions, dbt docs generate, lineage graph

dbt genereert automatisch een documentatie website met een interactieve lineage graph. Beschrijvingen voeg je toe in schema.yml.

yaml — beschrijvingen toevoegen
models: - name: fct_orders description: | Feitentabel met alle orders vanuit Salesforce. Bevat alle orders van status 'open' t/m 'refunded'. Wordt dagelijks bijgewerkt via incrementele refresh. meta: owner: "data-team@bedrijf.nl" contains_pii: false columns: - name: order_id description: "Unieke identifier per order (PK)" - name: amount_euros description: "Orderbedrag in euro's (geconverteerd vanuit cents)"

Documentatie genereren en bekijken

bash
# Genereer documentatie dbt docs generate # Start lokale webserver dbt docs serve # → opent http://localhost:8080 in je browser # Lineage graph bekijken: # Klik op een model → klik 'Lineage' tabblad
ℹ️ Met dbt Cloud kun je de documentatie automatisch publiceren na elke productie run. De lineage graph toont de volledige afhankelijkheidsketen van bronnen tot eindmodellen.
9

CI/CD met GitHub Actions

Automatisch testen bij pull requests, productie deploy

Automatiseer je dbt runs met GitHub Actions zodat elk pull request getest wordt en productie alleen wordt bijgewerkt na goedgekeurd code review.

yaml — .github/workflows/dbt-ci.yml
name: dbt CI on: pull_request: branches: [main] jobs: dbt-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Python setup uses: actions/setup-python@v5 with: python-version: '3.11' - name: Installeer dbt run: pip install dbt-snowflake - name: dbt dependencies run: dbt deps env: DBT_PROFILES_DIR: . - name: dbt build (slim CI) run: | dbt build \ --select state:modified+ \ --defer \ --state ./prod-artifacts env: SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }} SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_USER }} SNOWFLAKE_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }} SNOWFLAKE_DATABASE: ${{ secrets.SNOWFLAKE_DATABASE }} SNOWFLAKE_WAREHOUSE: ${{ secrets.SNOWFLAKE_WAREHOUSE }}
Slim CI: Met --select state:modified+ en --defer bouw je alleen gewijzigde modellen en hun afhankelijkheden. Dit bespaart significant op rekentijd en kosten.
10

Checklist

Controleer alle stappen voor je naar productie gaat
Installatie & Setup
  • dbt geïnstalleerd met juiste adapter
  • dbt debug toont geen fouten
  • profiles.yml buiten de git repository
  • .gitignore bevat target/, dbt_packages/, .env
Modellen
  • Sources gedeclareerd in schema.yml met freshness checks
  • Staging modellen doen alleen casting en hernoemingen
  • ref() gebruikt in plaats van directe tabelnamen
  • Incrementele modellen hebben een unique_key
Tests & Kwaliteit
  • Alle primaire sleutels hebben unique + not_null tests
  • Alle foreign keys hebben relationships tests
  • dbt test passeert zonder fouten
  • dbt source freshness geeft geen errors
Documentatie & CI/CD
  • Alle modellen hebben een description in schema.yml
  • dbt docs generate werkt zonder fouten
  • GitHub Actions workflow actief op pull requests
  • Productie secrets als GitHub Secrets opgeslagen