dbt Staging Layer Materialisation in BigQuery: Fix the Cost Leak
Choosing the wrong materialisation for dbt staging models is one of the most common — and most fixable — sources of runaway BigQuery costs. Here's the framework to get it right.
The Short Answer: Your dbt Staging Layer Materialisation Is Probably Wrong
The wrong materialisation strategy on dbt staging models in BigQuery causes every downstream model to re-scan your largest source tables on every run — silently multiplying your query costs with each additional mart or report you build. The fix is a deliberate, layer-by-layer materialisation decision, not a project-wide default. If you have not explicitly configured materialisation for your staging layer, BigQuery is almost certainly doing more work than it needs to.
This is one of those problems that feels invisible until the bill arrives. You build out your dbt project, add a few marts, connect a BI tool, and watch your BigQuery spend creep upward quarter after quarter. Nobody changed anything. Nobody ran a particularly expensive query. But costs tripled — because every new downstream model is re-reading the same raw source tables through staging views that were never designed to carry that load.
In our work with growth-stage fintechs and e-commerce businesses, the staging layer materialisation decision is almost always the first place we look when a BigQuery cost review surfaces unexplained spend. It is rarely the only problem, but it is almost always a problem.
What Does dbt Staging Layer Materialisation Actually Mean — and Why Does It Matter in BigQuery?
In a standard dbt project, models are organised into layers: staging (raw source data cleaned and typed), intermediate (business logic joins and transformations), and marts (the final aggregated models consumed by BI tools). Each layer can be materialised differently — as a view, a table, an incremental model, or an ephemeral model.
The default dbt materialisation is view. That means unless you override it, every staging model is a virtual query that runs against your raw source tables every time any downstream model references it.
One of the most impactful design decisions in dbt is how you materialise each layer of your DAG. Most teams understand this in principle but skip the decision in practice — they keep the project default as view for staging, accept it as "lightweight", and move on.
The problem is what happens at scale. If a staging model feeds multiple downstream marts, recomputation can quietly increase BigQuery costs. A staging view over a 200GB events table that feeds eight downstream models is not "lightweight" — it is a 200GB scan executed eight times per pipeline run. If that pipeline runs hourly, you have done 192 full-table scans of that events table in a single day, before you count any ad-hoc BI queries on top.
The cost compounds further because of how BigQuery prices on-demand queries. A common issue that arises is the increased size of tables in BigQuery due to repeated transformations, which can significantly inflate costs, as each transformation run generates additional queries. A staging view does not store anything — but it forces BigQuery to re-execute the base query from raw source data each time, which means you pay for the bytes scanned on every reference.
For connected reading on how BigQuery handles CTE re-evaluation — which compounds this problem inside staging models themselves — see our post on BigQuery CTEs Re-evaluated Every Time: The dbt Cost You're Missing.

📺 Watch: Explained about Incremental Models & Incremental Strategy in DBT #dbt
Why Do Teams End Up With the Wrong Staging Materialisation?
The honest answer is that most teams arrive at their staging materialisation by accident. They followed a tutorial that used view as the default, it worked during development, and it shipped to production without a second thought.
There are three specific failure modes we see repeatedly in the field:
Failure Mode 1: Project-wide view default with no overrides.
The dbt_project.yml sets +materialized: view for all models and nobody overrides it at the staging layer. This is the most common pattern and the one that stings hardest at scale. Every staging model becomes a live query against raw source tables, and every downstream model inherits that scan cost.
Failure Mode 2: Staging models materialised as table without partitioning or clustering.
Some teams overcorrect. They realise their staging views are expensive to re-execute, switch everything to table, and then discover their BigQuery storage and compute costs have jumped in a different direction — because every dbt run rewrites full staging tables from scratch, scanning all source data on every execution. In a typical dbt workflow, every transformation is essentially a SQL query that reads data from BigQuery tables and writes the output into new or existing tables. Without incremental logic and without partitioning on the staging table, you have traded one cost problem for another.
Failure Mode 3: Overuse of ephemeral models at the staging layer. Ephemeral models inline their SQL directly into the calling model at compile time. This can look efficient on the surface — no storage cost, no materialised table — but when a shared staging ephemeral model is referenced by multiple downstream models, its full SQL is inlined into each one. The result is that BigQuery executes the same base query independently in each downstream context, with no shared computation. We cover this trap in detail in our post on dbt Ephemeral Models in BigQuery: The Hidden Cost Trap.
A pattern we see repeatedly in pre-Series B companies: the data team started small with three or four marts, staging views were fine, and nobody revisited the materialisation strategy as the project grew to forty models. By that point, every pipeline run is quietly scanning terabytes it does not need to.
The Decision Framework: How to Choose the Right Materialisation for Each Staging Model
This is not a one-size-fits-all answer. The right materialisation for a staging model depends on four factors: source table size, downstream fan-out, run frequency, and whether the source data is append-only or mutable.
Here is the framework we use when auditing a dbt project:
Step 1 — Audit your staging models for downstream fan-out.
For each staging model, count how many downstream models reference it (directly via ref(), or indirectly through intermediate models). Any staging model with three or more downstream consumers is a strong candidate to be materialised as a table or an incremental model rather than a view. Doing all these transformations in the intermediate layer allows you to use them in different downstream models and simplify the complexity of dimension and fact models. The same logic applies one layer earlier — a staging model that feeds multiple intermediates should not be a view.
Step 2 — Classify source tables by volume. Identify the raw source tables that your staging models sit on top of. Anything under 1GB is unlikely to cause a meaningful cost problem regardless of materialisation. Tables above 10GB that are scanned repeatedly should be materialised immediately.
Step 3 — Choose the right materialisation by case:
view: Use only for staging models on small source tables (under ~1GB) with limited downstream consumers (one or two). Views are genuinely cheap here and avoid unnecessary storage. Temp tables often perform better for repeated operations on big data, while CTEs are lightweight. The same principle applies to views versus materialised staging models.table: Use for staging models on large source tables (10GB+) where the source is fully refreshed on each load and where incremental logic is not straightforward. Addpartition_byandcluster_byto minimise downstream scan costs. BigQuery offers you the ability to partition or cluster your tables, improving performance and reducing costs for high-volume tables.incremental: The right choice for append-only event tables and transactional data — the most common pattern in fintech and e-commerce. Only new rows are processed on each run, dramatically reducing both compute and scan costs as tables grow.ephemeral: Reserve for small utility logic that is genuinely used by only one downstream model and has no meaningful source table size. Do not use ephemeral at the staging layer if the model is shared.
Step 4 — Set materialisation at the folder level in dbt_project.yml, not model by model.
Once you have made the decision, encode it structurally. Set the default materialisation for your staging folder, then override exceptions explicitly. This prevents the pattern where new staging models inherit a wrong default because a developer did not think to add a config block.
models:
your_project:
staging:
+materialized: view # default for small/simple staging
payments: # override for high-volume payment events
+materialized: incremental
ledger:
+materialized: table
+partition_by:
field: created_date
data_type: date
If you are looking to implement a full materialisation strategy across your dbt project in BigQuery, explore how Fintel Analytics approaches this — we work with fintech, payments, and e-commerce businesses to audit, redesign, and optimise exactly this kind of data infrastructure.

What Does Getting This Wrong Actually Cost?
The numbers are easier to reason about than most teams expect. Consider a Series A payments company with a staging model over a 50GB raw transactions table, materialised as a view, referenced by six downstream marts. The pipeline runs every hour.
With the view materialisation, every pipeline run executes six queries that each scan the full 50GB — 300GB of data processed per pipeline run. At 24 runs per day, that is 7.2TB processed daily from a single staging model, before any ad-hoc BI queries. At BigQuery on-demand pricing of $6.25 per TB (2026), that is $45 per day — or roughly $1,350 per month — from one staging model.
Switch that same staging model to incremental with a created_at timestamp filter, and each pipeline run processes only new rows since the last execution. At a typical transaction volume for a growth-stage payments company, that might be 500MB per hour rather than 50GB. The same six downstream models now process 3GB per run instead of 300GB — a 100x reduction in bytes scanned from that one model alone.
We have seen this play out directly with clients. A global fintech that came to us with a BigQuery bill that had tripled over two quarters had, at the root, four staging models over high-volume event tables that were still materialised as views — each one feeding between four and eight downstream models. Rematching those four models to incremental with appropriate filters and partition alignment cut their pipeline compute costs by over 60% in the first month, without changing a single downstream model or BI report.
This is also the scenario where related query-level inefficiencies compound quickly. If those same staging views contain repeated subqueries or non-filtered window functions, you are paying for both the re-execution of the full source scan and the inefficient SQL within it. For a detailed look at how repeated subqueries compound costs in dbt, see our post on Repeated Subqueries in dbt BigQuery: Stop Paying Twice.
How to Audit Your Current dbt Staging Layer in BigQuery
You do not need a full data platform review to identify whether your staging materialisation is costing you. A focused audit takes less than a day and surfaces the highest-impact changes immediately.
1. Query the INFORMATION_SCHEMA to find your most scanned tables.
Run the following in BigQuery to identify which models are driving the most bytes processed in the last 30 days:
SELECT
referenced_table.table_id AS source_table,
COUNT(*) AS query_count,
SUM(total_bytes_processed) / POW(1024, 4) AS total_tb_processed,
SUM(total_bytes_processed) / POW(1024, 4) * 6.25 AS estimated_cost_usd
FROM
`region-eu`.INFORMATION_SCHEMA.JOBS,
UNNEST(referenced_tables) AS referenced_table
WHERE
creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
AND job_type = 'QUERY'
AND state = 'DONE'
GROUP BY 1
ORDER BY 3 DESC
LIMIT 20;
Cross-reference the top results against your dbt staging models. If the most-scanned tables are your raw source tables — the ones your staging views sit directly on top of — you have found the problem.
2. Map staging model fan-out in your dbt DAG.
Use dbt ls --select +mart_model_name to trace which staging models feed which marts. Any staging model that appears as an ancestor to three or more marts is a priority for rematerialisation.
3. Check for missing incremental_strategy and filter predicates.
If you already have some incremental staging models, verify they have a correct is_incremental() filter. An incremental model without a proper filter predicate behaves identically to a full-refresh table on most runs — it still scans the full source on every execution.
4. Review your dbt_project.yml for accidental project-wide defaults.
Confirm that your staging folder does not inherit a view materialisation from a top-level default without any folder-level override. This is the single fastest fix — changing one line in dbt_project.yml can reclassify an entire staging folder.
Frequently Asked Questions
Q: Should dbt staging models always be materialised as views?
A: No — the "views are always cheaper" assumption breaks down quickly at scale. For staging models on large source tables (10GB+) with multiple downstream consumers, view materialisation causes full source scans on every downstream model execution. Incremental or table materialisation with partitioning is almost always more cost-efficient for high-volume staging layers in BigQuery.
Q: When should I use incremental models for dbt staging in BigQuery?
A: Use incremental models for staging when your source data is append-only (event logs, transaction records, API ingestion) and has a reliable timestamp or ID column you can filter on. Incremental staging dramatically reduces bytes scanned per run as source tables grow — it is the most impactful single materialisation change for growth-stage companies with transactional data.
Q: What is the performance difference between dbt view and table materialisation in BigQuery?
A: A view materialisation executes the staging query from scratch on every reference — the query planner re-reads the raw source table each time. A table materialisation writes the result once and downstream queries read from that pre-computed result, typically scanning far fewer bytes if the table is partitioned and clustered. For high-fan-out staging models over large sources, the difference can be one or two orders of magnitude in bytes processed.
Q: How does ephemeral materialisation affect BigQuery costs at the staging layer?
A: Ephemeral models inline their SQL into every downstream model at compile time. If a shared staging model is ephemeral and referenced by five mart models, BigQuery executes five separate full-source scans — one per mart — with no shared computation. Ephemeral materialisation is appropriate only for utility logic used by a single downstream model.
Q: How do I find which dbt staging models are driving the most BigQuery cost?
A: Query INFORMATION_SCHEMA.JOBS in BigQuery to identify the raw source tables with the highest bytes processed over the last 30 days, then cross-reference against your dbt staging model definitions. Any staging model that sits directly over a heavily-scanned raw table and is materialised as a view is the primary candidate for remediation.
The dbt staging layer materialisation decision is one of the highest-leverage, lowest-disruption optimisations available to any team running dbt on BigQuery — and it is consistently underestimated until the BigQuery bill forces a reckoning. At Fintel Analytics, we have helped fintech, payments, and e-commerce businesses identify and fix exactly this class of problem as part of focused data infrastructure audits, often recovering meaningful cost savings within the first week of engagement. If your BigQuery spend is climbing faster than your data volume justifies, the answer is almost always in the model structure — and it is fixable.
