Full refresh vs incremental in dbt - Performance Comparison
When using dbt, we often choose between full refresh and incremental runs.
We want to know how the time to run changes as data grows.
Analyze the time complexity of these dbt model configurations.
-- Full refresh
{{ config(materialized='table', full_refresh=true) }}
select * from source_table
-- Incremental
{{ config(materialized='incremental') }}
select * from source_table
where updated_at > (select max(updated_at) from {{ this }})
The first rebuilds the whole table every time. The second adds only new or changed rows.
Look at what repeats when running these models.
- Primary operation: Reading and processing rows from source_table.
- How many times: Full refresh reads all rows every run; incremental reads only new or updated rows.
Think about how the number of rows affects run time.
| Input Size (rows) | Full Refresh Operations | Incremental Operations |
|---|---|---|
| 10,000 | Processes all 10,000 rows | Processes only new rows (e.g., 100) |
| 100,000 | Processes all 100,000 rows | Processes only new rows (e.g., 1,000) |
| 1,000,000 | Processes all 1,000,000 rows | Processes only new rows (e.g., 10,000) |
Full refresh time grows directly with total data size. Incremental time grows with new data size, usually much smaller.
Time Complexity: O(n) for full refresh, O(k) for incremental where k << n
This means full refresh work grows with all data, incremental work grows only with new data added.
[X] Wrong: "Incremental runs always take the same time as full refresh."
[OK] Correct: Incremental only processes new or changed rows, so it usually runs faster as data grows.
Understanding how data size affects dbt runs helps you design efficient data pipelines and explain your choices clearly.
What if the incremental filter is missing or incorrect? How would that affect the time complexity?
Practice
What is the main difference between a full refresh and an incremental model in dbt?
Solution
Step 1: Understand full refresh behavior
A full refresh reloads the entire dataset, replacing all existing data.Step 2: Understand incremental behavior
An incremental model only adds or updates new or changed data, saving time.Final Answer:
Full refresh reloads all data; incremental loads only new or changed data. -> Option DQuick Check:
Full refresh = reload all, Incremental = load changes [OK]
- Confusing which method reloads all data
- Thinking incremental deletes all data first
- Assuming full refresh only runs once
Which of the following is the correct way to configure a dbt model for incremental loading?
-- model.sql
{{ config(
materialized = '???'
) }}Solution
Step 1: Identify materialization types in dbt
dbt supports 'table', 'view', 'incremental', and 'ephemeral' materializations.Step 2: Select the incremental materialization
To enable incremental loading, set materialized = 'incremental'.Final Answer:
'incremental' -> Option AQuick Check:
Incremental config = 'incremental' [OK]
- Using 'table' instead of 'incremental'
- Confusing 'view' with incremental
- Forgetting to set materialized property
Given this incremental model SQL snippet, what will happen when you run dbt run --full-refresh?
{{ config(materialized='incremental') }}
select id, updated_at, data
from source_table
{% if is_incremental() %}
where updated_at > (select max(updated_at) from {{ this }})
{% endif %}
Solution
Step 1: Understand
Running with--full-refresheffect--full-refreshforces dbt to drop and rebuild the entire incremental model.Step 2: Analyze the SQL logic
Theis_incremental()block is skipped during full refresh, so no filter applies; all rows are selected.Final Answer:
The entire table will be rebuilt fromsource_table. -> Option CQuick Check:
Full refresh rebuilds all data [OK]
- Thinking incremental filter still applies on full refresh
- Assuming model fails with is_incremental()
- Believing no data changes on full refresh
Identify the error in this incremental model SQL snippet:
{{ config(materialized='incremental') }}
select id, updated_at, data
from source_table
{% if is_incremental() %}
where updated_at > (select max(updated_at) from source_table)
{% endif %}
Solution
Step 1: Check the incremental filter logic
The filter uses max(updated_at) fromsource_table, which is the source, not the target incremental table.Step 2: Understand correct incremental filter
The filter should compare to max(updated_at) from the target table ({{ this }}) to avoid reloading all data.Final Answer:
The filter comparesupdated_atto max fromsource_tableinstead of the target table. -> Option AQuick Check:
Incremental filter must use target table max date [OK]
- Using source table max date in incremental filter
- Forgetting to use {{ this }} in subquery
- Confusing incremental and full refresh logic
You have a large sales table that updates daily. You want to use dbt incremental models to update only new sales but also occasionally rebuild the entire table to fix data issues. Which approach is best?
- Use
materialized='incremental'withis_incremental()filter onsale_date. - Run
dbt rundaily for incremental updates. - Run
dbt run --full-refreshwhen you need a full rebuild.
What is the main benefit of this approach?
Solution
Step 1: Understand incremental daily updates
Using incremental materialization with a filter onsale_dateloads only new sales daily, saving time.Step 2: Understand full refresh usage
Running with--full-refreshrebuilds the entire table to fix data issues when needed.Step 3: Identify main benefit
This approach balances efficiency and data correctness by updating incrementally and allowing full rebuilds.Final Answer:
It saves time by only processing new data daily and allows full rebuilds when needed. -> Option BQuick Check:
Incremental + full refresh = efficient and flexible [OK]
- Thinking full refresh runs every day
- Believing incremental never rebuilds data
- Assuming manual data deletion is required
Ready to prove it?
Pass the test and this pattern moves unknown
