Zum Hauptinhalt springen
dbt-Modelledbt-Transformationendata build tooldbt-Materialisierungendbt SQL

dbt-Modelle: Leitfaden zu Datentransformationen

dbt (data build tool) ist zum Standardwerkzeug des Analytics Engineering geworden, und das dbt-Modell ist sein grundlegender Baustein. Im Kern ist ein dbt-Modell nichts weiter als eine einzelne SELECT-Anweisung, gespeichert in einer Datei mit der Endung .sql. dbt nimmt dieses SELECT, umhüllt es mit dem passenden DDL und materialisiert es in Ihrem Data Warehouse oder Lakehouse als Tabelle, View oder inkrementelle Tabelle. Diese täuschend einfache Idee erschließt einen vollständigen Arbeitsablauf der Softwareentwicklung, also Versionsverwaltung, Tests, Dokumentation und Verwaltung von Abhängigkeiten, auf gewöhnlichem SQL, das Analyseteams ohnehin beherrschen.

TL;DR

Ein dbt-Modell ist eine Datei mit der Endung .sql und einer einzelnen SELECT-Anweisung. dbt übersetzt sie in Anweisungen vom Typ CREATE TABLE/VIEW und führt sie in der Reihenfolge der Abhängigkeiten aus, abgeleitet aus den Aufrufen von ref() und source(). Modelle werden in die Schichten Staging, Intermediate und Mart gegliedert, und jeder Lauf erzeugt maschinenlesbare Metadaten (manifest.json), die Plattformen für Data Governance wie Dawiso speisen.

Was ist dbt?

dbt ist ein quelloffenes Werkzeug für die Kommandozeile, daneben mit dbt Cloud auch ein verwalteter Dienst, und kümmert sich um das T in ELT, also um die Transformation von Daten, die bereits in Ihr Warehouse geladen wurden. Statt komplexe gespeicherte Prozeduren oder brüchige eigene Skripte zu pflegen, lässt dbt Ihr Team SELECT-Anweisungen schreiben und Abhängigkeiten zwischen ihnen ausdrücken. dbt übersetzt diese Anweisungen, löst den Graphen der Abhängigkeiten auf und führt die Transformationen in der richtigen Reihenfolge gegen Ihre Zieldatenbank aus (Snowflake, BigQuery, Databricks, Redshift, DuckDB und viele weitere).

Wichtige Fähigkeiten, die dbt in die Arbeit an Transformationen einbringt:

  • Selbsttätige Auflösung des DAG (gerichteter azyklischer Graph) aus den Aufrufen von ref() und source()
  • Eingebautes Framework für Tests mit Zusicherungen zur Datenqualität
  • Erzeugte Dokumentation mit interaktiven Graphen der Lineage
  • Jinja-Vorlagen für SQL nach dem DRY-Prinzip (Makros, Bedingungen, Schleifen)
  • Ein wachsendes Ökosystem an Paketen aus der Gemeinschaft (dbt_utils, dbt_expectations, AutomateDV)

Weil die gesamte Logik der Transformation in Dateien mit den Endungen .sql und .yml liegt, fügt sich dbt natürlich in Git ein und ermöglicht Code-Review, Branches, CI/CD und einen vollständigen Audit-Trail jeder Änderung.

Was ist ein dbt-Modell?

Ein dbt-Modell ist eine Datei mit der Endung .sql, die genau eine SELECT-Anweisung enthält. Der Dateiname wird zum Namen des Modells und damit zum Namen des Objekts, das dbt in Ihrem Warehouse anlegt.

-- models/staging/stg_orders.sql
select
    order_id,
    customer_id,
    status,
    cast(created_at as timestamp) as created_at,
    amount_cents / 100.0          as amount
from {{ source('raw', 'orders') }}

Wenn Sie dbt run ausführen, übersetzt dbt dies in eine Anweisung CREATE TABLE AS oder CREATE VIEW AS, je nach Materialisierung, und führt sie aus. Die Syntax mit den doppelten geschweiften Klammern ist Jinja-Templating: Sie lässt dbt zur Übersetzungszeit die richtigen Schemanamen einsetzen, Makros auswerten und Referenzen auflösen.

Modelle können wahlweise mit einem Jinja-Konfigurationsblock beginnen, der die Konfiguration auf Modellebene setzt:

-- models/marts/fct_orders.sql
{{
  config(
    materialized = 'incremental',
    unique_key    = 'order_id',
    on_schema_change = 'sync_all_columns'
  )
}}

select
    o.order_id,
    o.customer_id,
    c.country,
    o.amount,
    o.created_at
from {{ ref('stg_orders') }}      as o
left join {{ ref('stg_customers') }} as c using (customer_id)

{% if is_incremental() %}
where o.created_at > (select max(created_at) from {{ this }})
{% endif %}

Die Funktionen ref() und source()

Zwei Jinja-Funktionen sind für jedes dbt-Projekt zentral: ref() und source().

ref(): Verweise auf andere Modelle

{{ ref('model_name') }} teilt dbt mit, dass das aktuelle Modell von einem anderen Modell abhängt. Zur Übersetzungszeit ersetzt dbt den Aufruf durch den vollständig qualifizierten Namen aus Schema und Tabelle für die jeweilige Umgebung. Dabei geschehen zwei entscheidende Dinge:

  • Verfolgung der Abhängigkeiten: dbt fügt eine Kante in den DAG ein und stellt so sicher, dass das referenzierte Modell zuerst gebaut wird
  • Übertragbarkeit zwischen Umgebungen: Der aufgelöste Name wechselt selbsttätig zwischen den Schemata für Entwicklung, Staging und Produktion, ganz ohne fest verdrahtete Schemanamen

source(): Verweise auf Rohdaten

{{ source('source_name', 'table_name') }} verweist auf Rohtabellen, die nicht von dbt erzeugt wurden. Sources werden in Dateien mit der Endung .yml deklariert:

# models/staging/_sources.yml
version: 2

sources:
  - name: raw
    database: analytics
    schema: raw_data
    tables:
      - name: orders
        description: "Raw orders from the transactional database"
        loaded_at_field: _loaded_at
        freshness:
          warn_after: {count: 12, period: hour}
          error_after: {count: 24, period: hour}
      - name: customers
        description: "Raw customer records"

Deklarierte Sources erschließen Prüfungen der Aktualität (dbt source freshness), die Verfolgung der Lineage zurück bis zu den Rohtabellen und die Dokumentation der Ladeschicht neben den Modellen der Transformation.

Schichtmuster der dbt-Modelle: Sources, Staging, Intermediate, Marts Schichtmuster der dbt-Modelle SOURCE raw.orders SOURCE raw.customers SOURCE raw.products STAGING stg_orders STAGING stg_customers STAGING stg_products INTERMEDIATE int_orders enriched MART (FACT) fct_revenue MART (DIM) dim_customers Source (Rohtabelle) Staging (1 je Source, View) Intermediate (Join/Anreichern) Mart (Fakt/Dimension, Table)
Zum Vergrößern klicken

Materialisierungen von Modellen

Eine Materialisierung legt fest, wie dbt das Ergebnis des SELECT eines Modells im Warehouse ablegt. dbt bringt vier eingebaute Materialisierungen mit.

View

Die leichteste Möglichkeit: dbt legt eine View in der Datenbank an. Es werden keine Daten gespeichert, denn Abfragen gegen die View führen stets das darunterliegende SQL erneut aus. Views sind die voreingestellte Materialisierung.

  • Am besten für: Staging-Modelle, einfache Transformationen, selten abgefragte Modelle
  • Abwägung: Die Antwortzeit entspricht den Kosten dafür, das SELECT jedes Mal neu auszuführen

Table

dbt verwirft bei jedem Lauf die physische Tabelle und legt sie neu an. Alle Zeilen werden von Grund auf neu berechnet.

  • Am besten für: Modelle der Mart-Schicht, mittelgroße Datenbestände, bei denen eine vollständige Neuberechnung vertretbar ist
  • Abwägung: Bauzeit und Rechenaufwand wachsen mit der Datenmenge, und Teilaktualisierungen gibt es nicht

Incremental

Beim ersten Lauf baut dbt die vollständige Tabelle. Bei den folgenden Läufen verarbeitet es nur neue oder geänderte Zeilen und führt sie in die bestehende Tabelle ein.

{{
  config(
    materialized  = 'incremental',
    unique_key    = 'event_id',
    incremental_strategy = 'merge'
  )
}}

select
    event_id,
    user_id,
    event_type,
    occurred_at
from {{ source('raw', 'events') }}

{% if is_incremental() %}
  where occurred_at > (select max(occurred_at) from {{ this }})
{% endif %}
  • Am besten für: große Faktentabellen, Ereignisströme, überwiegend anhängende Datenbestände
  • Abwägung: aufwendigere Logik, und falsche Filterbedingungen können zu fehlenden Datensätzen oder zu Dubletten führen

Ephemeral

Ephemere Modelle werden überhaupt nicht materialisiert. dbt fügt ihr SQL als Common Table Expression (CTE) in jedes nachgelagerte Modell ein, das auf sie verweist.

  • Am besten für: Zwischenlogik für Berechnungen, die Sie wiederverwenden wollen, ohne sie zu speichern
  • Abwägung: nicht unmittelbar abfragbar, dazu aufgeblähte CTEs in komplexen nachgelagerten Modellen

Gliederung der Modelle: Staging, Intermediate und Marts

Das am weitesten verbreitete Muster für dbt-Projekte gliedert die Modelle in drei Schichten und hält die Logik der Transformation dadurch modular, prüfbar und nachvollziehbar.

Die Schicht Staging (models/staging/)

Ein Staging-Modell je Quelltabelle. Aufgabe eines Staging-Modells ist es, Rohdaten zu säubern und zu vereinheitlichen, also Spalten umzubenennen, Datentypen zu wandeln, Nullwerte zu behandeln und leichte fachliche Regeln anzuwenden. Staging-Modelle verweisen ausschließlich auf source(), nie auf andere Modelle. Üblicherweise werden sie als Views materialisiert.

-- models/staging/stg_customers.sql
select
    id                           as customer_id,
    lower(trim(email))           as email,
    first_name,
    last_name,
    cast(created_at as timestamp) as created_at,
    is_active
from {{ source('raw', 'customers') }}

Die Schicht Intermediate (models/intermediate/)

Modelle der Schicht Intermediate verbinden Staging-Modelle und reichern sie an. Sie halten fachliche Logik fest, die mehrere Marts teilen, die aber noch nicht unmittelbar veröffentlicht werden soll. Sie verweisen per ref() auf Staging-Modelle oder auf andere Modelle dieser Schicht.

-- models/intermediate/int_orders_enriched.sql
select
    o.order_id,
    o.amount,
    o.created_at,
    c.customer_id,
    c.email,
    c.country,
    p.product_name,
    p.category
from {{ ref('stg_orders') }}    as o
join {{ ref('stg_customers') }} as c using (customer_id)
join {{ ref('stg_products') }}  as p using (product_id)

Die Schicht Marts (models/marts/)

Marts sind die abschließenden, für die Auswertung bereiten Ergebnisse, gegliedert nach fachlicher Domäne (Finanzen, Marketing, Produkt). Sie stellen saubere, breite Tabellen bereit, die für Werkzeuge der BI und für freie Abfragen optimiert sind. Üblicherweise werden sie als Tabellen oder als inkrementelle Modelle materialisiert.

-- models/marts/finance/fct_revenue.sql
select
    date_trunc('month', created_at) as month,
    country,
    category,
    sum(amount)                     as revenue,
    count(distinct order_id)        as order_count
from {{ ref('int_orders_enriched') }}
group by 1, 2, 3

Namenskonventionen

Einheitliche Benennung gehört zu den wirksamsten Praktiken in dbt-Projekten. Die verbreitete Konvention nutzt Präfixe, um Schicht und Art eines Modells anzuzeigen:

  • stg_: Staging-Modelle, eines je Quelltabelle: stg_orders, stg_customers
  • int_: Modelle der Schicht Intermediate: int_orders_enriched, int_session_attribution
  • fct_: Faktentabellen, transaktional und mit additiven Kennzahlen: fct_orders, fct_page_views
  • dim_: Dimensionstabellen mit beschreibenden Merkmalen: dim_customers, dim_products
  • rpt_: Modelle der Berichtsschicht für bestimmte Anwendungsfälle in der BI, eine wahlweise zusätzliche Schicht

Dokumentation mit schema.yml

dbt erzeugt eine durchsuchbare Seite mit Dokumentation, und zwar aus Beschreibungsdateien mit der Endung .yml, die neben den Modellen liegen. Jedes Modell und jede Spalte kann eine Beschreibung tragen, die neben den selbsttätig abgeleiteten Graphen der Lineage erscheint.

# models/marts/finance/_finance.yml
version: 2

models:
  - name: fct_revenue
    description: >
      Monthly revenue aggregated by country and product category.
      Primary source of truth for the Finance dashboard.
    columns:
      - name: month
        description: "First day of the calendar month (UTC)."
      - name: revenue
        description: "Sum of order amounts in USD for the period."
        tests:
          - not_null
      - name: order_count
        description: "Distinct count of orders contributing to revenue."
        tests:
          - not_null

Der Leitfaden zu dbt-Tests zeigt ausführlich, wie Zusicherungen zur Qualität in schema.yml eingerichtet werden.

Modelle ausführen

Die wichtigsten Befehle für die Arbeit mit Modellen:

# Run all models
dbt run

# Run a specific model
dbt run --select stg_orders

# Run a model and all its upstream dependencies
dbt run --select +fct_revenue

# Run a model and all its downstream dependents
dbt run --select fct_revenue+

# Build models + run tests in one command (recommended for CI)
dbt build --select +fct_revenue

dbt build ist der empfohlene Befehl für Strecken der CI/CD: Er führt Modelle, Seeds, Snapshots und Tests in der Reihenfolge der Abhängigkeiten aus und bricht früh ab, wenn ein Test fehlschlägt, noch bevor nachgelagerte Modelle gebaut werden.

Lineage und Governance

Einer der am meisten unterschätzten Züge von dbt sind die maschinenlesbaren Metadaten, die als Nebenprodukt der gewöhnlichen Entwicklungsarbeit entstehen. Jeder dbt run erzeugt eine manifest.json mit dem vollständigen übersetzten Graphen und eine run_results.json mit den Ergebnissen des Laufs, einschließlich Laufzeiten und Zeilenzahlen. dbt docs generate erzeugt eine catalog.json, die jede Spalte jedes Modells beschreibt.

Diese Artefakte sind die Grundlage für die Anbindung an Plattformen für Datenkatalog und Governance. Dawiso liest dbt-Manifeste ein und füllt damit selbsttätig seinen Datenkatalog mit Beschreibungen der Modelle, Definitionen der Spalten, Beziehungen der Lineage und der Abdeckung durch Tests. Aus der Dokumentation, die Ihr Team in schema.yml schreibt, wird so ohne doppelte Arbeit ein durchsuchbarer und geregelter Katalog für das Unternehmen. Über das Katalogisieren hinaus erlauben die Metadaten aus dbt es Dawiso, die Lineage auf Spaltenebene zu zeigen, also welche Quellspalten in welche Mart-Spalten fließen, dazu die Abdeckung der Datenqualität, also welche Modelle Zusicherungen aus Tests tragen, und die Zusagen zur Aktualität, also wann jedes Modell zuletzt erfolgreich gebaut wurde.

In Aktion erleben

Interactive Data Lineage

Sichtbar machen, wie Daten sich bewegen, sich transformieren und über Systeme, Anwendungen und Berichte hinweg zusammenhängen.

Ein Cookie am Tag hält schlechte UX fern.

Wir verwenden Cookies, um Inhalte und Werbung zu personalisieren und unseren Traffic zu analysieren. Außerdem geben wir Informationen zu Ihrer Nutzung unserer Website an unsere Partner für Werbung und Analyse weiter, die sie mit weiteren Daten zusammenführen können, die Sie ihnen bereitgestellt haben oder die sie bei Ihrer Nutzung ihrer Dienste gesammelt haben. Mit einem Klick auf „Alle akzeptieren“ erlauben Sie uns, Cookies für Analyse und Werbung über Google Tag Manager zu verwenden. Sie können Cookies auch einzeln einstellen.

Einstellungen zur Zustimmung anpassen

Wir verwenden Cookies, um Inhalte und Werbung zu personalisieren und unseren Traffic zu analysieren. Außerdem geben wir Informationen zu Ihrer Nutzung unserer Website an unsere Partner für Werbung und Analyse weiter. Datenschutzerklärung

Notwendige Cookies ermöglichen zentrale Funktionen der Website, etwa die Anmeldung und die Kontoverwaltung. Ohne unbedingt notwendige Cookies lässt sich die Website nicht richtig nutzen.

Funktionale Cookies speichern Informationen zu Besuchern der Website, etwa Sprache, Zeitzone oder erweiterte Inhalte.

Analyse-Cookies zeigen, wie Besucher die Website nutzen. Sie können nicht dazu verwendet werden, einen bestimmten Besucher direkt zu identifizieren.

Wir verwenden Microsoft Clarity, um zu sehen, wie Sie unsere Website nutzen (einschließlich Heatmaps und Sitzungsaufzeichnungen), damit wir sie verbessern können. Mit der Nutzung unserer Website stimmen Sie zu, dass wir und Microsoft diese Daten erheben und verwenden. Details finden Sie in unserer Datenschutzerklärung.

Werbe-Cookies erkennen Besucher über verschiedene Websites hinweg, etwa bei Content-Partnern oder Bannernetzwerken. Unternehmen können damit ein Profil der Interessen eines Besuchers aufbauen oder auf anderen Websites passende Werbung anzeigen.