← All cheatsheets
Data Engineer · #046 · September 7, 2026 · 2 min read

What is dbt and how does it actually work?

Models as SELECTs, ref() and the DAG it builds, two-line data tests, staging vs marts, and the three materializations that cover real projects: dbt on one page.

Get the free PDF

One page, print-ready, free to share. No signup needed.

Download the PDF

The T in ELT has a name, and it is the most-cited transform tool in data engineering job postings. dbt is just SQL files, run in the right order. Here is the whole idea on one page. The print-ready A4 PDF is at the bottom.

The idea

  • A model = a SELECT: one .sql file per table.
  • ref('stg_orders'): never a raw table name.
  • dbt builds the DAG: the run order comes from the refs.

Commands

  • dbt run: build the models.
  • dbt test: check the data.
  • dbt build: both, in DAG order.
  • dbt docs generate: lineage for free.

Tests

  • unique, not_null: two lines of yaml.
  • accepted_values: enum columns.
  • relationships: foreign keys, checked on every run.

Layers

  • staging/: rename, cast, clean.
  • marts/: what the BI tool reads.
  • sources.yml: the raw tables, declared once.

Materializations

  • view: the default, always fresh.
  • table: heavy models read often.
  • incremental: big fact tables, append new rows only.

A whole model, honestly

-- models/marts/daily_revenue.sql
SELECT
  order_date,
  SUM(amount) AS revenue,
  COUNT(DISTINCT customer_id) AS buyers
FROM {{ ref('stg_orders') }}
WHERE status = 'complete'
GROUP BY order_date

This file IS the table. No CREATE TABLE, no DROP, no run order to remember: dbt reads the refs, orders the builds, and writes the DDL.

The trap: bypassing ref()

You wroteWhat happensWrite instead
FROM analytics.stg_ordersinvisible to the DAGFROM {{ ref('stg_orders') }}
SELECT * from stagingupstream drift hits martsname the columns
no tests on the martbad join ships to the BI toolunique + not_null minimum

ref() is the entire product: lineage, run order, environments. Hardcode a name and you opt out of all three.

Frequently asked questions

What is a dbt model?
One .sql file containing one SELECT statement: that file IS the table or view. No CREATE TABLE, no DROP, no run order to remember. dbt reads the ref() calls inside your models, derives the dependency graph from them, and writes the DDL itself when you run dbt run or dbt build.
What does ref() do in dbt?
ref('stg_orders') replaces a hardcoded table name with a reference dbt can trace. Those references are the entire product: they give you lineage, the build order, and environment switching for free. Write FROM analytics.stg_orders instead and the model becomes invisible to the DAG, so it works today and breaks the day the raw table moves.
How do dbt tests work?
You declare them in yaml next to the model: unique and not_null on a key column take two lines, accepted_values covers enum columns, and relationships checks foreign keys on every run. dbt test runs them against real data, and dbt build runs models and tests together in DAG order, so a bad join fails before it reaches the BI tool.
What is the difference between staging and marts in dbt?
Staging models rename, cast and clean the raw tables declared in sources.yml, one staging model per source table. Marts are what the BI tool reads: the joined, aggregated, business-facing tables built on top of staging. Clean first, serve second, and never let a mart read a raw table directly.

Get the free PDF

One page, print-ready, free to share. No signup needed.

Download the PDF

More cheatsheets