dbt (Data Build Tool)
dbt verwaltet Datentransformationen im Warehouse mittels SQL. Version 2.0 führt die Fusion Engine (Rust) für verbesserte Leistung ein.
Wann verwenden
- Analytics Engineering & Datentransformationen: Transformation von Rohdaten in modernen Cloud-Warehouses (Snowflake, BigQuery, Databricks, Redshift).
- Modulares SQL mit Jinja & Versionskontrolle: Erstellung wiederverwendbarer SQL-Modelle mit DAG-Abhängigkeiten und Makros.
- Datenqualität & Schema-Tests: Durchsetzung von
unique,not_null, referenzieller Integrität und benutzerdefinierten Tests. - Automatisierte Datendokumentation & Abhängigkeiten: Generierung interaktiver Abhängigkeitsdiagramme direkt aus Codebasis-Schemas.
Schnellstart
-- models/marts/fct_orders.sql
{{ config(materialized='table') }}
with orders as (
select * from {{ ref('stg_orders') }}
),
payments as (
select * from {{ ref('stg_payments') }}
)
select
orders.order_id,
orders.customer_id,
orders.order_date,
coalesce(payments.amount, 0) as total_amount
from orders
left join payments using (order_id)
Kernkonzepte
#Deklarative SQL-Modellierung mit ref() & source()
Erstellung von Transformationsmodellen mit Abhängigkeitsauflösung:
-- models/marts/core/fct_customer_orders.sql
{{
config(
materialized = 'incremental',
unique_key = 'order_id',
on_schema_change = 'fail'
)
}}
with orders as (
select * from {{ ref('stg_orders') }}
{% if is_incremental() %}
where order_date >= (select coalesce(max(order_date), '1970-01-01') from {{ this }})
{% endif %}
),
customers as (
select * from {{ ref('stg_customers') }}
)
select
o.order_id,
o.customer_id,
c.full_name as customer_name,
o.order_date,
o.total_amount_usd
from orders o
inner join customers c on o.customer_id = c.customer_id
#Schema-Tests & Dokumentation in YAML
Durchsetzung von Spaltenbeschränkungen und Dokumentation:
# models/marts/core/schema.yml
version: 2
models:
- name: fct_customer_orders
description: "Tägliche Kundenbestell-Faktentabelle, die Bestellungen aus Staging und Kundendimensionen kombiniert."
columns:
- name: order_id
description: "Primärschlüssel für Bestellungen."
tests:
- unique
- not_null
- name: customer_id
description: "Fremdschlüssel, der auf Staging-Kunden verweist."
tests:
- not_null
- relationships:
to: ref('stg_customers')
field: customer_id
- name: total_amount_usd
description: "Gesamttransaktionswert in USD."
tests:
- not_null
#Jinja-Makros für DRY wiederverwendbare Logik
Erstellung benutzerdefinierter wiederverwendbarer SQL-Hilfsfunktionen:
-- macros/cents_to_dollars.sql
{% macro cents_to_dollars(column_name, decimal_places=2) -%}
round(cast(({{ column_name }} / 100.0) as numeric), {{ decimal_places }})
{%- endmacro %}
-- Verwendung im Modell:
select
id,
{{ cents_to_dollars('amount_in_cents') }} as amount_usd
from {{ ref('stg_payments') }}
Häufige Muster
Inkrementelle Modelle mit Zeitstempelfilterung
Problem: Der vollständige Neuaufbau von Tabellen mit Milliarden Zeilen bei jedem dbt-Lauf ist langsam und teuer.
Lösung:
Verwende incremental-Materialisierung mit is_incremental():
{{ config(
materialized='incremental',
unique_key='event_id'
) }}
select * from {{ source('raw', 'events') }}
{% if is_incremental() %}
-- Nur Zeilen abfragen, die nach dem aktuellsten Event in der Zieltabelle erstellt wurden
where event_timestamp > (select max(event_timestamp) from {{ this }})
{% endif %}
Best Practices (2026)
- Tun: Organisiere Projekte in standardisierte Schichten:
staging(1:1 mit Rohquellen),intermediateundmarts. - Tun: Verwende immer
{{ ref('model_name') }}und{{ source('source_name', 'table_name') }}, um die DAG-Abhängigkeit zu erhalten. - Tun: Implementiere inkrementelle Modelle (
materialized='incremental') für Faktentabellen mit Millionen von Zeilen. - Tun: Führe
dbt testin CI-Pipelines bei jedem Pull-Request aus, um Schema-Regressionen zu erkennen. - Nicht tun: Schreibe rohe Datenbanktabellenreferenzen (z.B.
analytics.raw.users); verwende immersource()oderref(). - Nicht tun: Führe Geschäftslogik in Staging-Modellen aus; Staging sollte nur Spalten bereinigen, umwandeln und umbenennen.
- Nicht tun: Hartcodiere Umgebungen (dev vs. prod); verwende
target.name-Bedingungen in profiles.yml.
Fehlerbehebung
| Fehler | Ursache | Lösung |
|---|---|---|
Compilation Error: Model '...' depends on a node named '...' which was not found | Tippfehler in {{ ref('model_name') }} oder Modelldatei fehlt. | Überprüfe, ob der Modellname mit dem referenzierten String exakt übereinstimmt. |
dbt test failed: unique constraint violated | Doppelte Schlüssel durch nicht-eindeutige Join-Bedingung. | Untersuche doppelte Schlüssel im kompilierten SQL in target/compiled/. |
Schema change error during incremental run | Upstream-Modell hat Spalten hinzugefügt/entfernt, die die bestehende inkrementelle Tabelle brechen. | Führe einmalig --full-refresh aus, um das Schema neu aufzubauen: dbt run --full-refresh. |