Start learning free

LESSON 53 OF 107 · DATA ENGINEERING: DBT

View or Table?

Practice

When dbt build runs a model, it saves the result in the warehouse. How it saves it is the model's materialization. With no setting, a model is a view: dbt stores only the SELECT, and the warehouse runs it again each time someone reads the view, so it always shows the raw rows as they are now. A table stores the rows themselves at build time: reading it is fast, but it shows the data of the last build until the next one. Data teams pick one per model: light staging models stay views, and marts that people and dashboards read all day become tables.

You choose with a config line at the top of the model, in Jinja's {{ }}: dbt reads it and leaves it out of the SQL. Its two values here are 'view' and 'table'. For example, a products model that should stay a view:

{{ config(materialized='view') }}

SELECT product, SUM(quantity) AS sold
FROM {{ ref('stg_orders') }}
GROUP BY product

You edit models/marts/daily_revenue.sql, the daily revenue mart. It reads this given staging model, models/staging/stg_orders.sql, which keeps the last copy of each order:

SELECT
    order_id,
    customer_id,
    product,
    TRY_CAST(quantity AS INTEGER) AS quantity,
    CAST(unit_price AS INTEGER) AS unit_price,
    status,
    CAST(ordered_at AS TIMESTAMP) AS ordered_at
FROM {{ source('shop', 'raw_orders') }}
QUALIFY ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY filename DESC) = 1

The check builds the project and asks DuckDB what each model became. Then a loader adds orders/2026-05-08.csv to raw_orders (two paid orders worth 24 and 29, and one cancelled), and the check compares daily_revenue's totals with the paid orders of stg_orders, before and after the next build. That is why the mart needs an orders column: the number of paid orders each day. Your code must print:

model stg_orders (view): OK
model daily_revenue (table): OK
table_name | table_type
daily_revenue | BASE TABLE
stg_orders | VIEW
(2 rows)
The loader added orders/2026-05-08.csv, no dbt build yet:
model | paid_orders | revenue
daily_revenue | 64 | 2226
stg_orders | 66 | 2279
(2 rows)
After dbt build:
model stg_orders (view): OK
model daily_revenue (table): OK
model | paid_orders | revenue
daily_revenue | 66 | 2279
stg_orders | 66 | 2279
(2 rows)

Until the second build, the table keeps the 64 orders of the first one, while the view stg_orders already sees 66. At work you type dbt build in a terminal, and the warehouse is Snowflake or BigQuery instead of a DuckDB file; there a table also saves money, since a dashboard reading it doesn't rerun the whole query.

Your task: add an orders column that counts each day's paid orders, then make daily_revenue a table with config.


Assignment
Follow the Coding Tutorial to count each day's orders and store daily_revenue as a table.


Hint
A model with no config line is a view. COUNT(*) counts the rows in each day's group, and the WHERE already keeps paid orders only. The config line goes first in the file, on its own line.


STEP 1 OF 2
    Ctrl + Enter to check