ANALYSES

An analysis is a SQL file that DVT compiles but never runs — no table, no view, no side effects. It lives in your project, uses the same Jinja as your models, and gives ad-hoc queries a version-controlled home instead of a graveyard of untracked editor tabs.

A WORKING EXAMPLE

Drop a .sql file into an analyses/folder at the project root (create the folder if it isn't there — dvt initdoesn't scaffold it, and dbt's default analysis-paths picks it up automatically). {{ ref() }} and {{ source() }} work exactly as they do in a model:

-- analyses/revenue_by_cohort.sql
-- Compiled, never run: DVT writes the SQL, you decide where to use it.

with first_orders as (

    select
        customer_id,
        min(order_date) as first_order_date
    from {{ ref('fct_orders') }}
    group by customer_id

)

select
    date_trunc('month', f.first_order_date) as cohort,
    date_trunc('month', o.order_date)       as revenue_month,
    sum(o.amount)                           as revenue
from {{ ref('fct_orders') }} o
join first_orders f using (customer_id)
group by 1, 2
order by 1, 2

Compile it — nothing touches a warehouse, and with --select the result prints straight to your terminal:

$ dvt compile --select revenue_by_cohort

Found 14 models, 1 analysis, 2 seeds, 21 sources
Concurrency: 4 threads (target='pg_dev')

Compiled node 'revenue_by_cohort' is:
with first_orders as (

    select
        customer_id,
        min(order_date) as first_order_date
    from "analytics"."public"."fct_orders"
    group by customer_id

)
...

Every {{ ref() }} resolved to a real relation on your default target. The same executable SQL is also written to disk, ready to paste into a BI tool, hand to a colleague, or run by hand:

target/compiled/<project_name>/analyses/revenue_by_cohort.sql

WHAT YOU GET FOR FREE

Because an analysis is a real node in the project, it inherits everything a model has except execution:

  • Full Jinja — ref(), source(), var(), and your own macros all render.
  • DAG membership — an analysis refs models, so lineage knows exactly what it reads. It is always a leaf: models cannot ref an analysis back, which is precisely why it can never break your builds.
  • Version control — the query that answered last quarter's board question is in git, not in somebody's clipboard.
  • Selection — dvt compile --select <name> targets one analysis, and dvt ls lists them alongside your models.

You can document analyses the same way you document models — a properties file with an analyses: key, giving each a description that shows up in your project docs.

ANALYSIS OR MODEL?

One question decides it: does anything downstream need the result as a relation? If yes, it's a model. If you only need the SQL— to read, share, or run once — it's an analysis.

YOU WANT TOUSE
Build a table or view that other models, tests, or dashboards reada model — it materializes and joins the DAG
Keep a one-off investigation, audit query, or training example in gitan analysis — compiled, reviewable, never built
Draft a future model and see its compiled SQL before committing to itan analysis first — promote it to models/ when it earns a place in the DAG
Answer a question across engines with a result that lands somewherea federated model (f_table) — analyses never execute, so they can't federate

Promotion is a file move: analyses/revenue_by_cohort.sql models/marts/revenue_by_cohort.sql, and the next dvt runbuilds it. The SQL doesn't change — that's the point of drafting it as an analysis.

TARGET FLIPS TAKE ANALYSES WITH THEM

An analysis compiles against the default target, so its SQL is written in that target's dialect — a date_trunc here, a ::date cast there. That makes analyses exactly the kind of file that quietly breaks when a project moves engines.

In plain dbt, switching warehouses means porting every analysis by hand — dbt treats them as inert text, and dialect-specific SQL just stops compiling on the new engine. DVT's dvt flip-target-totranspiles analyses right alongside your models: each file is rewritten into the new target's dialect (Jinja untouched, a timestamped .bakbeside it), and anything the transpiler couldn't carry is named in the flip report — per file, with the line.

One asymmetry worth knowing: only models can be federated. An analysis never runs, so it never takes the f_table path — it is always judged in the target's dialect, and a flip always moves it to the new one. See switching targets for the full flip workflow.

REFERENCE

FACTVALUE
Locationanalyses/ at the project root — dbt's analysis-paths default. Override with analysis-paths: [...] in dbt_project.yml.
File typePlain .sql files with Jinja. No {% analysis %} block wrapper exists — the whole file is the query.
Jinja supportFull: ref(), source(), var(), macros, control flow.
MaterializationNone, ever. Analyses are compiled, not run — config(materialized=...) does not apply.
Compiled outputtarget/compiled/<project_name>/analyses/<file>.sql, written by dvt compile. --select <name> also prints the compiled SQL to stdout.
DAG positionLeaf node. Analyses can ref models and sources; nothing can ref an analysis.
DocumentationA properties .yml with an analyses: key — name, description, columns.
DialectThe default target's — always. Analyses cannot be federated.
On a target flipTranspiled to the new target's dialect by dvt flip-target-to, like models — with a .bak per file and any transpiler limitation named in the report.