What if you could see every change your data ever went through, like a time machine for your database?
Why Slowly changing dimensions (SCD Type 2) in dbt? - Purpose & Use Cases
Imagine you have a customer database where people move, change jobs, or update their preferences. You try to track these changes by manually updating records in spreadsheets or simple tables.
Every time a customer changes their address, you overwrite the old one, losing the history of where they lived before.
Manually updating records means you lose important history. You can't see what the data looked like before the change.
This makes it hard to answer questions like "Where did the customer live last year?" or "How did their preferences evolve?"
Also, manual updates are slow and prone to mistakes, especially when data grows large.
Slowly changing dimensions Type 2 solves this by keeping a full history of changes.
Instead of overwriting, it adds new records with start and end dates, so you can see every version of the data over time.
This makes tracking changes easy, accurate, and automated using dbt models.
UPDATE customers SET address = 'New Address' WHERE customer_id = 123;
UPDATE customers_scd2 SET end_date = '2023-12-31', current_flag = FALSE WHERE customer_id = 123 AND current_flag = TRUE; INSERT INTO customers_scd2 (customer_id, address, start_date, end_date, current_flag) VALUES (123, 'New Address', '2024-01-01', NULL, TRUE);
It enables you to track and analyze how data changes over time, unlocking powerful historical insights.
A retail company uses SCD Type 2 to track customer address changes, so they can send promotions based on where customers lived during past holiday seasons.
Manual updates overwrite history and lose valuable data.
SCD Type 2 keeps full change history with start/end dates.
dbt automates this process for reliable, scalable data tracking.
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
