pip install dbt-core dbt-<adapter>Core + one warehouse adapter (-snowflake,-bigquery,-postgres,-duckdb…).dbt init <project>★Scaffold a new project + set up your profile.dbt debug★Check the warehouse connection & config are valid.dbt depsInstall packages frompackages.yml. Re-run after edits.~/.dbt/profiles.ymlConnection secrets (host, schema, credentials) — kept out of the repo.dbt_project.ymlThe project's config file (name, paths, model defaults).models/ · seeds/ · snapshots/ · tests/ · macros/The standard folder layout dbt expects.
select ... from ...★Every model is a singleSELECT— no DDL, dbt adds it.{{ ref('other_model') }}★Reference another model — this builds the DAG. Never hardcode names.{{ source('raw', 'orders') }}★Reference a declared raw table — the DAG's entry point.{{ config(materialized='table') }}★Per-model settings at the top of the file.{{ this }}The current model's own relation (for incremental logic).dbt compile -s my_modelSee the raw SQL dbt generates — great for debugging Jinja.
materialized='view'★Default.CREATE VIEW— no storage, always fresh, recomputed on read.materialized='table'★CREATE TABLE AS— fully rebuilt each run; fast to query.materialized='incremental'★Insert/merge only new rows into an existing table (see card 04).materialized='ephemeral'No object — inlined as a CTE into downstream models.materialized='materialized_view'Warehouse-managed MV (Snowflake/BigQuery/Databricks).+materialized: tableSet defaults per folder indbt_project.ymlinstead of per file.
{% if is_incremental() %} ... {% endif %}★Filter block — only applied on runs after the first.where updated_at > (select max(updated_at) from {{ this }})The classic "only rows newer than what's loaded" pattern.unique_key='id'★Enables updates (merge) instead of duplicate inserts.incremental_strategy='merge'merge·append·delete+insert·insert_overwrite·microbatch.incremental_strategy='microbatch'1.9+Time-sliced batches viaevent_time/batch_size/lookback.on_schema_change='append_new_columns'How to react when the model's columns change.dbt run --full-refreshrebuildsDrop & rebuild from scratch — ignores the incremental filter.
dbt run★Build models only, in DAG order.dbt build★Seeds + models + snapshots + tests together (see card 06).dbt test★Run data tests on models/sources/snapshots/seeds.dbt seedLoadseeds/*.csvinto the warehouse as tables.dbt snapshotCapture SCD-2 history of a source (card 12).dbt docs generate && dbt docs serveBuild & browse the docs + lineage site.dbt retry★Re-run only the nodes that failed last time.dbt build --empty1.8+Build the schema with zero rows (LIMIT 0) — a fast "does it compile & wire up" dry run in CI.dbt ls · dbt clean · dbt parseList resources · wipe target/dbt_packages · parse-time profiling.
dbt runModels only. Tests are a separate step you must remember.dbt build★Runs each resource then its tests before moving downstream.# build orderseed → test seed → model → test model → next model…dbt build --fail-fastStop at the first failure instead of continuing.why it mattersbuildstops bad data from flowing downstream; prefer it in production.
-s my_model★--select— run just this node.-s +my_model★Model and everything upstream (its parents).-s my_model+★Model and everything downstream (its children).-s +my_model+The full lineage in both directions.-s 2+my_modelOnly 2 degrees of ancestors (my_model+3= 3 down).-s @my_modelModel, its children, and all their parents too.-s stg_a stg_bSpace = union.a,b(comma) = intersection.--exclude tag:slowSubtract nodes from the selection.
-s tag:daily★By tag set inconfig(tags=[...]).-s path:models/stagingBy folder path.-s source:raw_shopify+Everything built from a source.-s config.materialized:incrementalBy any config value.-s state:modified+ --state ./prod★Only what changed since a saved manifest + downstream — CI's workhorse.-s result:error+ --state ./targetRe-run last run's failures and their children.dbt run --defer --state ./prodBorrow prod's upstream tables so dev doesn't rebuild the world.
sources: - name: raw★Declare raw schemas/tables in a.ymlundermodels/.tables: - name: ordersEach raw table you'll reference.{{ source('raw', 'orders') }}★Reference it in models — compiles to the real relation.freshness: warn_after: {count: 12, period: hour}Flag stale data past a threshold.loaded_at_field: _loaded_atColumn freshness is measured against.dbt source freshness★Check whether upstream data is up to date.
models: - name: fct_orders★A YAML entry per model (any.ymlundermodels/).description: "One row per order"Shows up in the docs site.columns: - name: order_idDocument + attach tests per column.config: {materialized: table, tags: [finance]}Configure the model from YAML instead of in-file.meta: {owner: data_team}Arbitrary metadata surfaced in docs.versions:Model versioning for safe, staged contract changes.
name: jaffle_shop · profile: defaultProject name + which profile to connect with.models:★Configure whole folders with+-prefixed keys.staging:
+materialized: viewEvery model understaging/becomes a view.+schema: staging · +tags: [stg]Target schema & tags per folder.vars: {start_date: '2024-01-01'}Project-wide variables, read withvar().on-run-end: ["grant usage ..."]Project hooks (alsoon-run-start).
seeds/countries.csv★Small CSV lookup tables — version-controlled with the code.dbt seedLoad them; then{{ ref('countries') }}like any model.{% snapshot snap_orders %} ... {% endsnapshot %}★Capture how source rows change over time (SCD-2).strategy='timestamp', updated_at='updated_at'Detect change by a timestamp column.strategy='check', check_cols='all'Detect change by hashing columns — no timestamp needed.dbt_valid_from · dbt_valid_toAuto-added columns bounding each version's lifespan.
data_tests: [unique, not_null]★The two you'll use most — attach under a column.- accepted_values: {values: [a, b]}Column may only contain these values.- relationships: {to: ref('dim'), field: id}★Every value exists in a parent table (referential integrity).config: {severity: warn}Warn instead of error; alsowhere,limit.dbt_utils · dbt_expectationsPackage tests:unique_combination_of_columns,expect_*…dbt test --store-failuresPersist failing rows to a table to inspect them.
tests/assert_positive.sql★ASELECTthat returns bad rows — passes when it returns none.select * from {{ ref('fct') }} where amount < 0Any row here = a failure. That's the whole contract.unit_tests: - name: test_logic1.8+Test transformation logic with mock inputs.given: ... expect: ...★Feed fixed rows, assert exact output — no warehouse data needed.dbt test -s test_type:unitRun only unit tests (ortest_type:data).
{{ ... }} vs {% ... %}★{{ }}outputs a value;{% %}is logic (if/for/set).{{ var('start', '2024-01-01') }}★Read a project/CLI var with a default.{{ env_var('DBT_SCHEMA') }}Pull a value from the environment.{{ target.name }}Current environment (dev/prod) — branch on it.{% for col in [...] %} ... {% endfor %}Loop to generate repetitive SQL.{% set x = ... %} · {{ log(x, info=True) }}Assign a variable · print during compile.{% set r = run_query(sql) %}Query the warehouse at compile time (needs 2 parse passes).
{% macro cents_to_dollars(col) %}★Define a function inmacros/; call it as{{ cents_to_dollars('x') }}.{% endmacro %}Close the block; args can have defaults.dbt run-operation grant_select --args '{role: rep}'Invoke a macro directly, outside any model.packages.yml★List packages;dbt depsinstalls intodbt_packages/.dbt_utils · codegen · dbt_expectations · audit_helperThe staples — helper macros, generators, extra tests.{{ dbt_utils.star(from=ref('x')) }}e.g. select all columns except a few — no hand-typing.
dbt docs generate && dbt docs serve★Generate the catalog + browse the interactive DAG.{% docs my_block %} markdown {% enddocs %}Reusable doc block; reference with{{ doc('my_block') }}.exposures:Declare downstream dashboards/apps so they appear in lineage.pre_hook / post_hook★Run SQL before/after a model (grants, indexes, vacuum).{{ config(post_hook="grant select on {{ this }} ...") }}Common use: grant on the freshly built relation.persist_docs: {relation: true, columns: true}Push descriptions into the warehouse's own metadata.
dbt CoreThe open-source Python engine (this sheet'sdbt-core 1.12).dbt Fusion2026New engine in Rust — faster & stricter, now the default install. Same project language & DAG.v2 eraFusion requires all deprecation warnings resolved; DAG semantics carry over unchanged.dbt platform / CloudHosted IDE, orchestration, Semantic Layer, docs & catalog.dbt --version · require-dbt-versionCheck yours; pin a project's compatible range.Semantic Layer · MetricFlowDefine metrics once in YAML; query them consistently everywhere.