dbt
Intermediate 15 min read dbt

dbt Models

SELECT once, materialize everywhere

A dbt model is a SQL SELECT that dbt turns into a table or view. Models read from sources or other models via ref(), and their dependencies are resolved and documented automatically.

dbtmodelsref()materializationviewtabledependency graph
Before we start

What you will learn

  • Write a model that refs a source and one that refs another model
  • Choose between view, table, and incremental materializations
  • Use the dependency graph to reason about lineage
Introduction

What is this?

A model is the unit of transformation in dbt: a plain SQL SELECT stored in a file. dbt wraps it in materialization logic so it becomes a view, a table, or an incrementally updated table in the warehouse. Using ref('other_model') instead of a raw table name makes every dependency explicit, testable, and lineage-visible.

Motivation

Why do you need it?

  • ref() gives you a dependency graph and correct run ordering for free.
  • One SQL file = one source of truth instead of nested transformations in the warehouse.
  • Materialization is config, not logic — switching view to table is a keyword, not a rewrite.
Think about it

Mental model

Imagine

A chef calling for 'the stock'

Think of

A model using ref('stock')

The chef names the dish's dependency, not the brand of pot it came from, so the kitchen can change the recipe one place while guests still get the dish.

Mechanics

How it works

  • Write a SELECT that reads from {{ source('raw', 'orders') }} or {{ ref('stg_orders') }}.
  • dbt resolves refs into a dependency graph and orders model execution.
  • Materialization config decides the output: view, table, or incremental.
  • Downstream models can now ref() this one, forming the lineage chain.

Models ref each other into a graph dbt executes in dependency order.

Sources
Staging
Marts
Downstream
Real world

In production

  • An orders mart refs stg_orders and stg_customers, so the join lives in one place.
  • A heavy weekly aggregation materializes as a table; a lightweight alias as a view.
  • Renaming an upstream column happens in the staging model, and every consumer updates via ref().
Guidelines

Best practices

  • Always use ref() for model-to-model dependencies; never hard-code table names.
  • Materialize views for light logic, tables for heavy aggregations, incremental for append-heavy facts.
  • Give models names that carry meaning and the layer prefix; avoid _final and _v2 suffixes.
Watch out

Common mistakes

  • Using source() for business-level queries instead of building a staging model first.
  • Re-reading the same source table in ten models when one staging model would do.
  • Over-use of incremental materialization that complicates pipeline logic for small data.
When things break

Debugging tips

  • If a model returns too many rows, its source joins multiplied — check for fan-out at ref boundaries.
  • If lineage looks wrong, search for direct table names in the SQL that should be ref().
Interviews

Interview checkpoint

Practice

Hands-on lab

Build a Staging-to-Mart Model Chain
40 min · intermediate
In progress
Objectives
  • Write a staging model for raw orders
  • Write a mart that refs the staging model
  • Run, inspect lineage, and change materialization

Build a Staging-to-Mart Model Chain

40 min · intermediate

0/4 steps
0%
sandbox · Linux sandbox
$
Try these commands
Test yourself

Quick quiz

0/2
1

The right way to reference another model in dbt is

2

A model that re-reads a source table directly instead of the staging model

Go deeper

Official documentation