dbt Tutorial for Beginners: Learn dbt, the Data Build Tool

A fast, practical dbt tutorial for beginners. Go from setup to your first dbt project with real SQL, ref(), tests, docs, snapshots, and incremental models.

This dbt tutorial for beginners gets you from zero to a working dbt project fast. You will install (or sign up for dbt Cloud), connect a data warehouse, scaffold a project, create dbt models with ref(), add tests, generate dbt docs, run a snapshot, and make a model incremental. Real commands and file contents. If you need to learn dbt quickly, follow this end-to-end path and ship something useful today.

What is dbt and why use it?

dbt (data build tool) is how analytics engineers and data teams manage SQL-based data transformation as code. It compiles models, executes them in your data warehouse, tracks data lineage, and provides documentation out of the box. This is an introduction to dbt that replaces scattered scripts with a clean, dependable data pipeline. Compared to running SQL from Python, dbt adds dependency graphs, configs, tests, and environments so you move faster with fewer mistakes.

If you want more context and related topics, browse the dbt topic hub.

Choose your setup: dbt Cloud vs dbt Core

Use dbt Cloud for a hosted IDE and scheduler, or dbt Core (open source) for a local CLI workflow. For a full breakdown, see dbt Cloud vs Core. Quick comparison:

OptionStart here if…ProsTrade-offs
dbt CloudBeginner, team-readyBrowser IDE, jobs, auth, easy dbt docsHosted, usage-based pricing
dbt Core (open source)Local CLI, power usersFull control, free to runDIY scheduler, more setup

Create your first dbt project

Option A: dbt Cloud in 5 minutes

  1. Sign up for dbt Cloud and create a project.
  2. Connect your data warehouse (Snowflake, BigQuery, Redshift, Postgres, etc.).
  3. Connect a Git repository (start with the built-in repo, push to GitHub later).
  4. Open the IDE and run dbt debug to validate.

Later, create a scheduled dbt job in Cloud to run builds and tests automatically.

Option B: Local with dbt Core (CLI)

  1. Create and activate a Python environment.
  2. Install dbt and your adapter (example: Postgres):
pip install dbt-core dbt-postgres
  1. Initialize a project:
dbt init my_first_project
cd my_first_project

Example profiles.yml for Postgres (in ~/.dbt/):

my_first_project:
  target: dev
  outputs:
    dev:
      type: postgres
      host: localhost
      user: dbt_user
      password: dbt_password
      port: 5432
      dbname: analytics
      schema: dbt_dev
      threads: 4

Validate:

dbt debug

Project structure you’ll see

my_first_project/
  dbt_project.yml
  models/
    staging/
    marts/
  snapshots/
  tests/
  macros/
  packages.yml

We’ll add sources, dbt models, tests, and docs next. This follows dbt fundamentals and is a solid starter template for small teams.

Connect raw tables with sources

Declare upstream tables once, then reference them consistently across your dbt project. Create models/staging/sources.yml:

version: 2
sources:
  - name: raw
    schema: raw
    tables:
      - name: orders
      - name: customers

Build your first model with ref()

Start with a staging model that cleans up raw.orders. Cast the right data types and standardize column names. Create models/staging/stg_orders.sql:

-- models/staging/stg_orders.sql
select
  id as order_id,
  customer_id,
  cast(order_total as numeric(12,2)) as order_total,
  status,
  created_at::timestamp as created_at
from {{ source('raw','orders') }}

Now build a downstream fact model that references staging. Create models/marts/fct_orders.sql:

-- models/marts/fct_orders.sql
select
  o.order_id,
  o.customer_id,
  c.email,
  o.order_total,
  o.status,
  o.created_at
from {{ ref('stg_orders') }} o
left join {{ source('raw','customers') }} c
  on c.id = o.customer_id

Run just these:

dbt run --select stg_orders fct_orders

Tip: use selectors to isolate work when your orders table has 40M rows and you need fast feedback.

Add basic tests (schema tests)

Create models/marts/schema.yml:

version: 2
models:
  - name: fct_orders
    columns:
      - name: order_id
        tests:
          - not_null
          - unique
      - name: customer_id
        tests:
          - not_null

Run tests:

dbt test --select fct_orders

That catches duplicates and missing keys immediately. For a deeper dive into test types and patterns, see dbt Tests: Complete Guide.

Generate dbt docs and view lineage

Build dbt docs once you have a few models:

dbt docs generate

Then serve locally and explore your graph:

dbt docs serve

This opens the UI with model descriptions, columns, tests, and data lineage. Telling teammates to “check dbt docs” gives them a canonical place to look.

Make a model incremental for speed

Large tables? Use an incremental materialization so only new or changed rows process after the first full load. Update models/marts/fct_orders.sql:

{{ config(
  materialized='incremental',
  unique_key='order_id'
) }}

select
  o.order_id,
  o.customer_id,
  c.email,
  o.order_total,
  o.status,
  o.created_at
from {{ ref('stg_orders') }} o
left join {{ source('raw','customers') }} c
  on c.id = o.customer_id

{% if is_incremental() %}
  where o.created_at > (select coalesce(max(created_at), '1900-01-01') from {{ this }})
{% endif %}

Run it:

dbt run --select fct_orders

After the first build, subsequent runs take the incremental path.

Snapshots for SCD2

Track changes in upstream dimension rows (e.g., customer email updates) using snapshots. Create snapshots/customers.sql:

{% snapshot customers_scd %}
{{ config(
  target_schema='dbt_dev',
  unique_key='id',
  strategy='timestamp',
  updated_at='updated_at'
) }}

select * from {{ source('raw','customers') }}

{% endsnapshot %}

Run:

dbt snapshot

Use the snapshot table to support correct historical joins and point-in-time analytics.

Packages: extend dbt with utilities

Packages are reusable projects that add macros, models, or both. Add packages.yml:

packages:
  - package: dbt-labs/dbt_utils
    version: "~>1.3"

Install:

dbt deps

Now you can call dbt_utils macros for convenience. For practical Jinja patterns and macro tips, see Macros & Jinja Tips.

Schedule, CI, and orchestration

In dbt Cloud, create a job that runs selected models and tests on a schedule. If you prefer orchestrators, compare options in dbt Cloud vs. Airflow. With GitHub, protect main, require a passing run on pull requests, and keep your repository clean.

Best practices that pay off immediately

  • Separate staging/ from marts/. Keep naming consistent.
  • Declare sources and test keys early; failures catch drift fast.
  • Prefer ref() over hard-coded schema.table for portable SQL.
  • Document columns as you go; future-you and every analyst will thank you.

These best practices compound into reliable analytics over time and are core to analytics engineering.

FAQ

# dbt Configs (single choice) — When you run dbt snapshot, which folder does dbt look in for the SCD2 tables?

The snapshots/ folder in your project.

Are you trying to learn dbt and understand what it’s all about?

dbt helps you manage data transformation as code. Start small: initialize a dbt project, build two models, add tests, generate dbt docs, then iterate. If you want to learn dbt pragmatically, this tutorial is enough to start.

Before we dive into the details, what exactly is dbt, and what purpose does it serve?

dbt compiles your project into warehouse-native SQL, executes it, and tracks dependencies so you treat transformations like software: versioned, tested, documented, and reproducible.

Dbt Packages | What is a package in data build tool?

A package is a published set of macros, models, or both that you reference in packages.yml and install with dbt deps. They speed up common work so you don’t reinvent wheels.

Do I need prior SQL knowledge to take this course?

Basic SQL is enough to start. If you’re a data analyst or a new data engineer, you can follow the examples and deepen skills as you go. For advanced testing, see the linked guide above.

From inside of a Docker container, how do I connect to the localhost of the machine?

Use host.docker.internal as the host on macOS/Windows. On Linux, run with --add-host=host.docker.internal:host-gateway or use the Docker gateway IP. Example Postgres DSN: postgresql://dbt_user:dbt_password@host.docker.internal:5432/analytics.

Frustrated at having to set up so many things before getting to what dbt is?

Start in dbt Cloud to skip local config, then move to the CLI if needed. You’ll write models in minutes and can revisit platform choices later.

If you were starting from scratch, how would you recommend someone go about learning dbt?

  1. Stand up a tiny project (two staging models, one mart).
  2. Add tests and docs, then a snapshot.
  3. Make one table incremental.
  4. Schedule a run and review results.

Also browse our dbt topic hub and reputable dbt courses for structured learning.

Moreover, how is it superior to executing SQL scripts within, for instance, Python code?

dbt brings ref()-based dependency graphs, environment configs, tests, documentation (dbt docs), and standardized deployment. You avoid brittle string manipulation and ad-hoc orchestration.

What can Jinja do?

Jinja templating lets you reuse logic, add control flow, parameterize configs, and build safer patterns. For hands-on patterns, see Macros & Jinja Tips.

Where to go next

That’s the core loop: model → test → document → run. You’ve just built maintainable transformations that support analytics. If you want graded, hands-on practice, try the free exercises at /practice.

Next steps

Take this concept into practice.

Reading is fine. Doing is what gets you hired. Pick an exercise on this topic or open a portfolio project.