Slowly changing dimensions (SCD Type 2) in dbt - Time & Space Complexity
When working with slowly changing dimensions type 2 in dbt, we want to know how the processing time grows as the data size increases.
We ask: How does the time to update and insert records change when the input data grows?
Analyze the time complexity of the following dbt SQL snippet for SCD Type 2.
WITH source_data AS (
SELECT * FROM {{ ref('source_table') }}
),
current_dim AS (
SELECT * FROM {{ ref('dimension_table') }} WHERE current_flag = 1
),
updates AS (
SELECT s.*
FROM source_data s
JOIN current_dim d ON s.key = d.key
WHERE s.attribute <> d.attribute
),
new_records AS (
SELECT s.*
FROM source_data s
LEFT JOIN current_dim d ON s.key = d.key
WHERE d.key IS NULL
)
SELECT * FROM updates
UNION ALL
SELECT * FROM new_records
This code finds changed and new records to update the dimension table with history.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Joining source data with current dimension data to find changes and new records.
- How many times: Each record in source data is compared once against dimension data.
As the number of source records grows, the number of comparisons grows roughly the same.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 comparisons |
| 100 | About 100 comparisons |
| 1000 | About 1000 comparisons |
Pattern observation: The work grows linearly as input size increases.
Time Complexity: O(n)
This means the time to process grows directly in proportion to the number of input records.
[X] Wrong: "The process will take the same time no matter how many records there are."
[OK] Correct: Each new record adds work because it must be checked and possibly inserted or updated, so time grows with data size.
Understanding how data volume affects processing time helps you explain and improve data pipeline efficiency in real projects.
"What if we added an index on the key columns? How would the time complexity change?"
Practice
Solution
Step 1: Understand SCD Type 2 concept
SCD Type 2 tracks changes by adding new rows instead of overwriting existing data.Step 2: Compare options with concept
Only To keep full history of changes by adding new rows for each change describes adding new rows to keep full history, which matches SCD Type 2.Final Answer:
To keep full history of changes by adding new rows for each change -> Option AQuick Check:
SCD Type 2 = Add new rows for history [OK]
- Confusing SCD Type 2 with Type 1 which overwrites data
- Thinking SCD deletes old records
- Assuming SCD aggregates data
Solution
Step 1: Identify incremental loading condition
Incremental models use a WHERE clause to filter new or changed rows based on a timestamp.Step 2: Match clause to SCD Type 2 incremental logic
WHERE updated_at > (SELECT MAX(updated_at) FROM target_table) filters rows with updated_at greater than the max in the target, enabling incremental load.Final Answer:
WHERE updated_at > (SELECT MAX(updated_at) FROM target_table) -> Option DQuick Check:
Incremental WHERE clause = WHERE updated_at > (SELECT MAX(updated_at) FROM target_table) [OK]
- Using GROUP BY instead of filtering new rows
- Ordering data without filtering
- Using HAVING without aggregation context
SELECT id, name, effective_from, effective_to, is_current FROM customer_scd WHERE is_current = TRUE
What will this query return?
Solution
Step 1: Analyze WHERE clause filtering
The query filters rows where is_current = TRUE, meaning only the latest active records.Step 2: Understand SCD Type 2 flags
In SCD Type 2, is_current marks the latest version; older versions have FALSE.Final Answer:
Only the latest active records for each customer -> Option CQuick Check:
is_current = TRUE returns latest records [OK]
- Assuming it returns all history
- Confusing effective_to NULL with is_current TRUE
- Ignoring the is_current flag meaning
WITH new_data AS ( SELECT * FROM source_table WHERE updated_at > (SELECT MAX(updated_at) FROM target_table) ), updated_records AS ( SELECT * FROM target_table WHERE is_current = TRUE ) SELECT * FROM updated_records UNION ALL SELECT * FROM new_data
What is the main issue causing duplicates?
Solution
Step 1: Understand SCD Type 2 update process
Old current records must be marked expired (is_current = FALSE) before adding new versions.Step 2: Identify missing expiration step
The query selects current records but does not update their is_current flag to FALSE, causing duplicates.Final Answer:
Missing logic to expire old current records before inserting new ones -> Option BQuick Check:
Expire old records before insert = Missing logic to expire old current records before inserting new ones [OK]
- Thinking UNION ALL causes duplicates here
- Believing WHERE clause is wrong without checking logic
- Assuming distinct needed without expiration logic
Solution
Step 1: Identify key SCD Type 2 columns
SCD Type 2 needs effective_from and effective_to timestamps plus a flag like is_current to mark active rows.Step 2: Compare options for completeness
customer_id, name, effective_from, effective_to, is_current includes customer_id, name, effective_from, effective_to, and is_current, covering history and current record tracking.Final Answer:
customer_id, name, effective_from, effective_to, is_current -> Option AQuick Check:
Effective dates + is_current = customer_id, name, effective_from, effective_to, is_current [OK]
- Using only timestamps without current flag
- Confusing deleted_flag with is_current
- Using version_number without effective dates
Ready to prove it?
Pass the test and this pattern moves unknown
