dbt

dbt CLI Core Commands: run / test / build / compile Differences and How to Use Them

2026-04-19
NicheeLab Editorial Team

Based on the stable behavior of dbt 1.x, this article walks through the differences between run, test, build, and compile so you can master them quickly for both the exam and real-world operations.

The two key points are: build is the umbrella command that runs seed, model, snapshot, and test together in DAG order, while compile only generates SQL and never modifies the database.

Overview: Responsibilities and Execution Order of the 4 Commands

dbt run builds models, and dbt test runs tests. dbt build is the umbrella command that builds seeds, models, and snapshots and then runs the matching tests per node. dbt compile only renders SQL with Jinja and refs resolved; it never touches the database.

For both the exam and day-to-day work, what matters is being crystal clear about what runs, in what order, and what side effects each command has. The table and diagrams below let you grasp roles and execution patterns at a glance.

  • run = builds only (no tests are executed)
  • test = test nodes only (0 rows means pass; any returned row means fail)
  • build = seed → model/snapshot → test, executed inclusively in DAG order
  • compile = no query execution; writes SQL under target/compiled
CommandTargetsRuns Tests?Main Side Effects
runmodel / seed / snapshot (depends on selectors)NoCreates and updates tables/views
testgeneric / singular testsYes (tests only)Stores failing rows (when store_failures is enabled)
buildseed, model, snapshot + attached testsYes (immediately after each node)Builds and validates in a single pass
compileRenders SQL for every nodeNoGenerates SQL under target/compiled

Core commands vs. DAG execution

sources --> seeds --> models --> tests
                 |          ^
                 v          |
              snapshots ----+

Legend:
- run:     executes seeds/models/snapshots only (no tests)
- test:    executes tests only
- build:   seeds → (models/snapshots) → tests, inclusively
- compile: no execution; only renders SQL

Quick usage examples

## Run just the models in dev
$ dbt run --select my_model+

## Run only the tests
$ dbt test --select tag:quality

## CI run on changed nodes only
$ dbt build --select state:modified+ --defer --state target

dbt run: Build-Only. Narrow the Scope with Selectors

dbt run executes models (and, depending on selectors, seeds/snapshots) in DAG order. It does not run tests, so when you need quality checks you have to combine it with test or use build.

Incremental models follow the materialized='incremental' logic and apply differential updates. Use --full-refresh when you need to rebuild from scratch.

  • Note that tests are NOT included (a common source of confusion on the exam)
  • Combine the --select and --exclude selectors
  • Switch between incremental updates and --full-refresh
  • Ephemeral models are not materialized; they are inlined into downstream SQL
ScenarioRecommended CommandWatch Out For
Partial build including dependenciesdbt run --select my_model+Mind the position of + (upstream vs downstream)
Full rebuilddbt run --full-refresh --select tag:factIncremental tables are re-created
Exclude a specific tagdbt run --select tag:mart --exclude tag:heavyBe deliberate about selection vs. exclusion ordering

How run executes

seed_a   model_b(ephemeral)
   \        |
    --> model_c (incremental)

run: seed_a → model_c (model_b is inlined; tests are not run)

Concrete selector examples

# Build the model and its downstream
$ dbt run --select my_model+

# Include the upstream (parents) too
$ dbt run --select +my_model

# Folder-based selection
$ dbt run --select models/marts/

# Exclude by tag
$ dbt run --select tag:daily --exclude tag:expensive

dbt test: Success Means “No Rows Returned”. Working with store_failures and severity

dbt test runs generic tests (not_null, unique, etc.) and singular tests (arbitrary SQL). The basic rule is: 0 rows means pass, 1 or more rows means fail.

Enable store_failures when you want to investigate failing rows after the fact. Use severity: warn for checks you want to treat as warnings rather than hard failures.

  • generic = declared in YAML; singular = written as SQL under tests/
  • Pass = 0 rows, Fail = 1+ rows (same rule for singular tests)
  • store_failures: true keeps a table of failing rows
  • severity: warn lets the pipeline keep going even on failures
Test TypeWhere/How DefinedPass Condition
genericmodels/schema.yml (not_null, unique, relationships, etc.)0 rows
singulartests/xxx.sql (any SELECT)0 rows

Test dependencies

model_x  →  test_not_null_model_x.col1
model_x  →  test_unique_model_x.col2

Tests depend on the target node; dbt test runs only the test nodes

schema.yml example (generic)

version: 2
models:
  - name: fct_orders
    columns:
      - name: order_id
        tests:
          - not_null
          - unique
      - name: customer_id
        tests:
          - relationships:
              to: ref('dim_customers')
              field: customer_id
              severity: warn
    config:
      materialized: incremental

# singular (tests/late_orders.sql)
-- 0 rows means pass
select *
from {{ ref('fct_orders') }}
where order_date > current_date + interval '1 day'

dbt build: The CI Default. Test Right After Each Node to Catch Breakage Immediately

dbt build builds seeds, models, and snapshots in DAG order and then runs the tests attached to each node right after it. This lets you catch broken changes before they propagate to downstream nodes.

To quickly verify just the impact of your changes, combine the state:modified selector with --defer so unchanged dependencies are delegated to production objects. Note that source freshness is a separate command (dbt source freshness).

  • Runs seed → model/snapshot → test on a per-node basis
  • state:modified+ scopes execution to changes plus their downstream
  • --defer resolves unchanged dependencies to a higher environment (faster runs)
  • source freshness is NOT part of build (run it separately)
GoalWith buildEquivalent split into run/test
Validate everything in one shotdbt builddbt seed && dbt run && dbt snapshot && dbt test
CI on changed nodes onlydbt build --select state:modified+ --defer --state targetdbt run --select state:modified+ && dbt test --select state:modified+

How build flows (per node)

[seed_s] → (test_seed_s)
   |             \
   v              X (skipped if the seed has no generic tests)
[model_m] → test_model_m_*
   |
   v
[snapshot_n] → test_snapshot_n_*

Slim CI build example

# Use the latest manifest.json for state selection
$ dbt build \
    --select state:modified+ \
    --defer \
    --state target \
    --profiles-dir . \
    --target ci

dbt compile: Lock In SQL Without Executing. Perfect for Reviews and Safety Checks

dbt compile resolves Jinja, ref, source, variables, and macros, then writes each node's SQL under target/compiled. It never executes or modifies anything in the database.

Because you can inspect the rendered SQL before execution, compile is ideal for reviews and static analysis. If you only need parsing there is also parse, but compile is more practical day-to-day because it actually outputs the final SQL.

  • No execution = no side effects (your production stays clean)
  • Output goes to target/compiled/<project>/<resource_type>/...
  • You can inspect the final post-macro SQL up front
  • Catches parser errors and unresolved references early
Use CaseArtifacts ProducedTypical Errors
Pre-PR reviewFinal SQL filesUndefined ref/source, Jinja syntax errors
Static analysis / lintingFull directory treeMacro argument mismatches, unresolved variables

compile output layout

project/
  models/
  macros/
  ...
      |
      v
 target/compiled/project/
   models/...
   snapshots/...
   tests/...

(No database execution happens)

Inspecting the rendered SQL

# Compile only
$ dbt compile

# Example: open the compiled SQL for the fct_orders model
$ sed -n '1,80p' target/compiled/<project>/models/marts/fct_orders.sql

Selectors and Dependencies: Patterns Common to Both the Exam and Production

Selectors give you precise control over which nodes are targeted. The + operator includes parent/child dependencies: a leading + means upstream (parents) and a trailing + means downstream (children).

A favorite exam topic: build automatically includes the tests attached to the selected nodes, but run does not. Combine resource type, tag, and path selectors to dial in a realistic granularity.

  • +my_model includes parents, my_model+ includes children, +my_model+ includes both
  • Resource types: model:, test:, seed:, snapshot:, source:
  • Combine tag:xxx, path:models/marts/, state:modified, and similar
  • build includes tests; run/test must be split explicitly
ExpressionNodes IncludedTypical Use
+ordersorders and its parents (upstream)Recompute from upstream to ensure consistency
orders+orders and its children (downstream)Verify downstream impact
model:stg_* tag:piiModels starting with stg_ AND tagged piiPartial runs by data classification

Intuitive view of dependency selection

src → stg_orders → int_orders → marts_orders
          ^              ^              ^
         +stg_orders   +int_orders    +marts_orders

my_model+ expands to the right (children)

Combination examples

# Parents and the node itself
$ dbt build --select +stg_orders --defer --state target

# Only the downstream of changed nodes
$ dbt build --select state:modified+ --defer --state target

# Exclude PII
$ dbt run --select tag:daily --exclude tag:pii

Check Yourself with a Question

Analytics Engineer

問題 1

In a PR you've changed only some models. You want unchanged dependencies to reference existing objects in a higher environment and only the changed nodes and their downstream to be built and tested. Which single command achieves this most efficiently?

  1. dbt build --select state:modified+ --defer --state target
  2. dbt run --select state:modified+ && dbt test --select state:modified+
  3. dbt compile --select state:modified+
  4. dbt test --select state:modified+ --store-failures

正解: A

build is the umbrella command that runs seed/model/snapshot for the selected nodes in DAG order and immediately runs tests too. state:modified+ scopes execution to changed nodes plus their downstream, and --defer with --state delegates unchanged dependencies to a higher environment. run does not include tests, so it cannot satisfy the requirement in one command; compile does not execute, and test alone does not build.

Frequently Asked Questions

What is the biggest difference between run and build?

run only builds models and similar resources, without running tests. build, on top of seed/model/snapshot builds, immediately runs the tests attached to each node. build is the default for CI and pre-production validation.

Does compile connect to or modify the database?

No. compile only resolves Jinja and refs and generates SQL; it never executes against or modifies the database. The output lands under target/compiled and is useful for code review and static analysis.

Is source freshness included in build?

No. Freshness checks are run separately via dbt source freshness. build targets seed, model, snapshot, and tests.

Check what you learned with practice questions

Practice with certification-focused question sets

無料で問題を解いてみる
Author

NicheeLab Editorial Team

NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.


Related articles
dbt

dbt Models: SQL-Defined Transformation Units (2026)

Model fundamentals — SELECT-based definitions, naming, refs,...

dbt

dbt Analytics Engineering Exam: Complete Guide (2026)

Pass the AE Certification — scope, weighting, sample questio...

dbt

dbt Cloud vs dbt Core: Feature & Cost Comparison (2026)

Honest comparison of dbt Cloud vs. dbt Core — IDE, scheduler...

dbt

dbt Project Structure: models/seeds/macros Layout (2026)

Recommended dbt project layout — models, seeds, macros, snap...

dbt

dbt_project.yml Explained: Every Config (2026)

Every dbt_project.yml setting that matters — paths, vars, ma...

Browse all dbt articles (101)
© 2026 NicheeLab All rights reserved.