Skip to content
LakeBench
ProblemsCommunityPricing
Sign inStart practicing

dbt & Analytics Engineering

Progress0/13
x

Analytics Engineering

  • What is analytics engineering?12m
  • dbt project structure12m
  • Jinja basics14m

What dbt Is

  • What dbt is12m
  • source() and ref() as compiled names12m

Staging

  • Staging models14m
  • not_null tests as SQL12m

unique and relationships

  • unique tests12m
  • relationships tests14m

Marts and Incremental

  • Marts from staging CTEs14m
  • Incremental vs full refresh12m
  • Views, tables, incrementals10m

Capstone

  • Capstone: staging, mart, test16m
Back to track
  1. Learn
  2. dbt & Analytics Engineering
  3. Analytics Engineering
  4. Jinja basics

Lesson 3 of 13 · Theory first, then run it

Jinja basics

dbtsqlbeginner14 min

Overview

{{ ref('stg_orders') }} becomes stg_orders after compile. {% if %} is template logic. This tab does not execute Jinja. You write the compiled SQL.

On this page7 sections›
  1. 1What you will do
  2. 2Why this skill
  3. 3How the code works
  4. 4Worked examples
  5. 5Common beginner questions
  6. 6What comes next
  7. 7Practice

What you will do

Jinja is a templating language. A template looks like SQL with extra marks: {{ ... }} prints a value, {% ... %} runs logic such as if and for. dbt uses Jinja inside model files so one SELECT can name other models, name sources, and change shape based on a variable.

Compile is the step that turns the template into plain SQL. {{ ref('stg_orders') }} becomes the relation name stg_orders (often with a schema in front). {% if %} branches disappear; only the SQL for the chosen branch remains. The warehouse never sees Jinja. It only sees the compiled SELECT.

The SQL editor in this tab does not execute {{ }} or {% %}. If you paste a template, the query fails. Read Jinja as a labeled template example, then write the compiled SQL in the exercise. That is the same substitution dbt would do.

Why this skill

Hardcoding FROM analytics.stg_orders in twenty marts means a rename is twenty edits. ref('stg_orders') is one name. dbt also reads those refs to build a graph: staging models run before marts that depend on them. Without Jinja refs, dbt cannot see the graph.

Logic in Jinja keeps warehouse-specific SQL in one file: a date function that differs on Snowflake versus Postgres, or a dev filter that limits rows. Used badly, Jinja becomes a second programming language nobody wants to debug. Used for refs, sources, and a few variables, it stays small.

How the code works

A paper form with blanks is the right picture. The blanks are {{ }}. Filling them in is compile. Mailing the filled form is running SQL in the warehouse.

Template, then compile, then SQL
Model file (SQL +Jinja)Compile (resolve refand if)Plain SQLWarehouse runsSELECT

Jinja never reaches the warehouse. Compile emits plain SQL. This tab starts after compile.

The two expressions you will see constantly:

Jinja in the model fileAfter compile (typical)Meaning
{{ ref('stg_orders') }}stg_ordersAnother model in this project
{{ ref('orders') }}ordersA model whose file is orders.sql
{{ source('jaffle', 'orders') }}jaffle.ordersA raw table you do not own
{% if target.name == 'dev' %} ... {% endif %}(the inner SQL, or nothing)Include a branch only when the condition is true

Worked examples

Run the example below in this tab. Read the input, follow the code, then check the output matches what you expect.

SQLJinja template (does not execute here)
-- TEMPLATE. Does not run in this tab. Label: Jinja template.
SELECT
    order_id,
    order_total AS amount
FROM {{ ref('stg_orders') }}
{% if target.name == 'dev' %}
WHERE order_id IS NOT NULL
{% endif %}

After compile, ref becomes a table name and the if either stays as SQL or vanishes. In this tab you write the compiled form yourself. For a model that is simply select * from {{ ref('orders') }}, compile is FROM orders.

SQLCompiled SQL (runs in this tab)
-- COMPILED. This is what you run here.
-- From the template: select * from {{ ref('orders') }}
SELECT *
FROM orders
LIMIT 8;
  1. Write the model with ref(), source(), and only as much {% if %} as you need.
  2. dbt compiles: substitutes names, evaluates Jinja, emits .sql the warehouse understands.
  3. dbt runs the compiled SQL (on your laptop, with the CLI).
  4. In this tab, skip to step 2's output: type the compiled SELECT.

Common beginner questions

Why does {{ ref('stg_orders') }} become stg_orders, not a file path?

ref names the model. dbt maps that name to the relation it created (a view or table). You query the relation, not the .sql file.

Can I use Jinja to loop over columns?

You can. Beginners should not start there. Prefer explicit column lists in staging. Heavy Jinja is hard to test and hard to read in code review.

What is the difference between ref and source?

source is for raw tables dbt does not build. ref is for models dbt does build. Marts should ref staging, not source raw, so cleanup lives in one place.

Leave {{ out of the editor

A code check in this lesson fails if your query still contains {{. Compile on paper: ref('orders') means the name orders, then write FROM orders.

The warehouse only sees SQL

Jinja is a dbt compile-time tool. Snowflake, BigQuery, and the SQL editor here all run the compiled statement. If the compiled SQL is wrong, the template was wrong.

What comes next

You can now read a model file and mentally compile it. The next lesson is what dbt is as a product: compiler, runner, and tester around those SELECT statements, still with honesty about what this tab will and will not do.

Practice

Run Sample to see compiled SQL for ref('orders'). Then complete the exercise: write SELECT * FROM orders LIMIT 8, the compiled form of select * from {{ ref('orders') }}. No Jinja in the editor.

Practicals · load into the editor

After you read the theory, run these in the pane on the right. They execute in this tab, no cluster.

Rate:
Was this useful?
dbt project structureWhat dbt is