In dbt, what is the difference between the view and table materializations?
answer
- one stores SQL, the other stores rows
- think about who pays the compute
- build time versus read time
- freshness is free for one of them
- create or replace view vs create table as
basics
~20 sIn dbt, a view materialization stores only the SQL, so every read recomputes the query; a table materialization runs the query once per dbt run and stores the rows, making reads fast but data only as fresh as the last build.
solid answer
~40 sBoth are the same `SELECT` — the materialization decides what dbt builds from it. With `materialized='view'` dbt issues `create or replace view`, which is near-instant to build and always reflects current source data, but every downstream query re-executes the whole transformation. With `materialized='table'` dbt issues `create or replace table ... as (select ...)`, paying the compute once per `dbt run` and storing the result, so consumers scan physical rows. I default to views for thin staging models and cheap logic, and switch to tables when the SQL is expensive, when many models or dashboards read it, or when it sits under a stack of other views. A table costs build time, storage, and staleness between runs; a view costs that compute again on every single read.
code
sql · 10 lines-- models/staging/stg_orders.sql
{{ config(materialized='view') }}
select
id as order_id,
customer_id,
cast(ordered_at as timestamp) as ordered_at,
amount_cents / 100.0 as amount
from {{ source('shop', 'orders') }}
where not is_testgo deeper
Be able to say what dbt actually issues for each: a create-or-replace view versus a create-table-as-select. Know that both are set with a config block and that the model SQL is identical either way.
Explain who pays the compute and when. Talk about build cost versus read cost, the staleness window a table introduces, and why deep chains of views over large scans get slow.
Show judgment about which layer gets which materialization across a project, and be ready to justify promoting an expensive intermediate model to a table when run time or dashboard latency data says so.
Own the convention: folder-level defaults in the project file, the criteria for promoting a model to a table, and how materialization choices trade warehouse spend against freshness and run-window length across many teams.
## What a materialization is in dbt A dbt model is a file containing one `SELECT` statement. dbt does not run that `SELECT` as-is — it wraps it in DDL determined by the model's **materialization** config. The materialization is the answer to "what object should exist in the warehouse when this model is built?" The built-in options are `view`, `table`, `incremental` and `ephemeral`. The same SQL can be flipped between them by changing one config line, with no edit to the query itself. You set it either in the model file: ```sql {{ config(materialized='table') }} select ... ``` or as a default for a folder in `dbt_project.yml`, where the model-file config wins over the project-file default. ## The view materialization With `materialized='view'`, dbt runs roughly `create or replace view analytics.stg_orders as (select ...)`. Nothing is computed at build time beyond parsing and validating the SQL, so `dbt run` over a hundred views finishes in seconds and costs almost no warehouse compute. The trade is deferred: a view is a stored query, so the transformation executes **every time someone selects from it**. If a dashboard hits the view a thousand times a day, you pay for that logic a thousand times. Views are also always current — they read whatever is in the underlying tables at query time, so there is no staleness window. A subtle cost is stacking. If `mart_revenue` is a view over `int_orders_joined`, which is a view over `stg_orders`, which is a view over the raw table, a single dashboard query expands into one large nested statement. Most warehouses optimize this reasonably well, but plans get harder to read, and deep view chains over big scans are a common cause of "the dashboard got slow and nobody changed anything." ## The table materialization With `materialized='table'`, dbt runs the equivalent of `create or replace table analytics.dim_customers as (select ...)`, fully rebuilding the object on every run. Reads afterwards are plain scans of stored, physically laid-out data — the warehouse can prune, cluster, cache and compress it, which a view cannot do because a view holds no data. The costs are three: build compute (the query runs in full every `dbt run`), storage, and **staleness** — the table reflects the source as of the last build, so a table refreshed hourly is up to an hour behind while its view sibling is never behind. There is also a brief availability consideration: on most warehouses `create or replace` is atomic enough that readers see either the old or the new version, but a long rebuild means the *content* is old for the duration of the run. ## How to choose Useful defaults that hold across most projects: - **Staging models** (light renaming, casting, filtering, one source each) → `view`. They are cheap to recompute and you want them to reflect the source. - **Intermediate models** → usually `view`, promoted to `table` when they are expensive or reused by several downstream models. - **Marts and BI-facing models** → `table`, because many people query them repeatedly and read latency is what users feel. Promote a view to a table when any of these is true: the SQL is expensive (large joins, window functions, `distinct` over big volumes); it is read far more often than it is built; it is the base of further views; or a BI tool queries it interactively and latency matters. Demote a table to a view when the build is dominating your run time, the model is rarely queried, or the freshness requirement is tighter than your run schedule. ## Where the other materializations fit When a table's full rebuild becomes too slow or expensive — typically a large append-mostly fact table — the next step is `incremental`, which builds the table once and then only processes new or changed rows on later runs. When a model is just reusable interim logic that nothing needs to query directly, `ephemeral` inlines it as a CTE and creates no object at all. Views and tables remain the two you reach for first: incremental adds real correctness complexity, and ephemeral hides logic from anyone outside dbt. ## Things that trip people up Switching a model from view to table (or back) is safe — dbt drops the existing object and creates the new type — but the *name* stays the same, so downstream `ref()`s keep working untouched. A model's materialization is not a property of its SQL; it is an operational decision you can revisit when run times or query patterns change. And because both are fully rebuilt each run, neither view nor table needs any thought about duplicates or late-arriving data — that concern belongs entirely to incremental models.
- If a view is always fresh and costs nothing to build, why not materialize everything as a view?Because you pay the transformation on every read instead of once per run. A view read by dashboards all day re-executes joins and aggregations each time, and views stacked on views turn one dashboard query into a deep nested plan. Tables let the warehouse store, prune and cache physical data, which is what makes interactive queries fast.
- What happens when you change a dbt model from materialized='view' to materialized='table'?On the next run dbt drops the existing view and creates a table under the same name and schema, so every downstream `ref()` keeps resolving without edits. The first run pays the full build cost. Nothing about the model's SQL changes — the materialization is an operational choice, not a property of the query.
- Where do you set a materialization so that a whole folder of dbt models shares it?In `dbt_project.yml` under the `models:` key, nested by project name and directory — for example every model in `models/staging` defaults to `+materialized: view`. A `{{ config(materialized=...) }}` block inside an individual model file overrides that folder default for just that model.
A view is a recipe card: cheap to write down, but somebody has to cook the dish every time it's ordered. A table is the cooked dish in the fridge: instant to serve, but you cooked it once and it only reflects the ingredients you had then.
saying these in an interview costs you the question
- Claiming a dbt view stores data or is faster to query
- Saying a table materialization keeps itself up to date automatically
- Thinking the materialization changes the model's SQL, not just the DDL
- Defaulting every model to table to make everything fast
- Assuming you must rewrite downstream refs when a materialization changes