What is the difference between an SCD Type 1 and an SCD Type 2 change in a dimension table?
answer
- one changes a value, one adds a row
- which one loses last year's answer?
- the fact holds a key from load time
- overwrite versus version
basics
~20 sA Type 1 change overwrites the attribute in place, so only the current value exists and history is lost. A Type 2 change retires the old row and inserts a new version, so old facts still report under the value that was true then.
solid answer
~50 sBoth are responses to a source attribute changing on a dimension member. **Type 1 overwrites**: the existing dimension row is updated in place, so there is exactly one row per member and every report — including last year's — recomputes under today's value. It is cheap, and it is right for corrections and cosmetic relabels. **Type 2 versions**: the load retires the current row and inserts a new row for the same business entity carrying the new value, so a member accumulates versions over time. Fact rows store the dimension's surrogate key that was current when they were loaded, so facts loaded before the change keep pointing at the old version and "as it was" reporting falls out for free. The price is a dimension that grows with change volume, change detection in the load, and counting distinct business keys rather than dimension rows.
code
text · 10 lines-- before: customer 4711 lives in Texas
cust_sk | cust_id | region | is_current
501 | 4711 | Texas | Y
-- after a Type 1 overwrite: same row, new value, old value gone
501 | 4711 | Oregon | Y
-- after a Type 2 change: old row retired, new version inserted
501 | 4711 | Texas | N
902 | 4711 | Oregon | Ygo deeper
Be ready to state both plainly: Type 1 updates the row and loses the old value, Type 2 adds a row and keeps it. Give one example where each is the right call, such as fixing a typo versus a customer relocating.
Explain the mechanism, not just the outcome: the fact row carries the surrogate key that was current when it loaded, which is why the old version keeps its facts. Mention the row growth and the change-detection work Type 2 adds.
Show judgment about consequences: which reports break under Type 1, that a Type 1 attribute cannot be retro-fitted with history, and how you hedge by retaining raw source snapshots. Note that the policy is per attribute, not per dimension.
Own the reporting semantics across teams: agreeing with finance what a historical number means, deciding what is worth versioning against dimension growth and query cost, and making the choice reversible by keeping immutable raw history upstream of the mart.
## What "slowly changing" means A dimension table describes the things a business measures *by*: customer, product, store, employee, sales rep. Its rows are mostly stable, but they are not immutable — a customer relocates, a product is moved to a new category, a rep is reassigned a territory. **Slowly Changing Dimension (SCD)** is the numbered taxonomy of what the warehouse does to the dimension table when one of those source attributes changes. Types 1 and 2 are the two poles of that taxonomy and the two an interviewer will always ask about. The decision is not a technical preference. It is a statement about what the business means by a historical number. ## Type 1 — overwrite The existing dimension row is updated in place. There is exactly one row per member, and it always shows the latest values. ```sql UPDATE dim_customer SET region = 'Oregon' WHERE cust_id = '4711'; ``` The consequence is that *all* history re-computes under the new value. If customer 4711 lived in Texas for three years and then moved to Oregon, a sales-by-region report now credits every one of those Texas years to Oregon. Sometimes that is exactly what you want — the value was simply wrong (a misspelled name, a mis-keyed category) or the label is cosmetic and nobody reports on it historically. Sometimes it is a silent falsification that finance will find out about at quarter close. Type 1 is the cheap option: no row growth, no key churn, no version bookkeeping, and current-state queries need no filters. ## Type 2 — a new row per change Instead of updating, the load retires the current row for that member and inserts a new one carrying the new attribute values. The member now has several rows in the dimension — each one a **version** of that member during some stretch of time. Versions are distinguished by the dimension's own surrogate key; the durable business key (`cust_id` above) repeats across all versions of the same member, which is how you know they are the same customer. The mechanism that makes history work is simple and worth saying out loud in an interview: **the fact table stores the surrogate key of the version that was current when the fact was loaded.** A sale from 2023 holds the key of the Texas version; a sale from 2025 holds the key of the Oregon version. Nothing is recomputed, nothing is re-derived — the join just lands on the right version. ```sql SELECT d.region, SUM(f.amount) AS sales FROM fact_sales f JOIN dim_customer d ON d.cust_sk = f.cust_sk GROUP BY d.region; ``` That query is "as it was" reporting, and it needed no special syntax. ## What Type 2 costs - **Row growth.** A dimension with volatile attributes multiplies: 40 million customers times a weekly-changing flag is not a dimension, it is a fact table. - **Counting.** `COUNT(*)` on the dimension is no longer a customer count; it is a version count. Count distinct on the durable business key, or restrict to current rows. - **Change detection.** The load has to compare incoming attribute values against the current version to know whether to version at all. - **Analyst confusion.** Someone will eventually ask why the customer appears twice. ## Choosing between them Ask one question, per attribute rather than per table: *does any report need the value as it was at the time of the fact?* If yes, Type 2. If the change is a correction — the old value was never true — Type 1, even inside a dimension that is Type 2 for other columns. Mixed policies within one dimension are normal and expected. ## What you cannot fix later Type 1 is destructive. Once the source has moved on and you have overwritten, the old value is gone from both sides and no amount of later engineering recovers it. Switching an attribute from Type 1 to Type 2 gives you history *from the cutover date forward only*. The practical hedge is to land raw source snapshots in an untransformed ingest layer, so a Type 2 dimension can be rebuilt retroactively if the requirement shows up later. ## Mistakes that get candidates marked down Calling a design Type 2 while the load still overwrites; updating already-loaded fact rows to point at the newest version (this destroys the entire point of versioning and turns your Type 2 dimension into an expensive Type 1); treating dimension rows as members when counting; and applying Type 2 to a high-churn attribute without considering the row explosion.
- An attribute has been Type 1 for two years and finance now wants history on it. What can you actually give them?History from the cutover date forward, and nothing before it — the prior values were overwritten in the warehouse and the source system almost certainly does not keep them either. The only rescue is untransformed raw snapshots of past loads, if the ingest layer retained them; from those you can rebuild versions. Be explicit about the gap rather than shipping a dimension that silently implies full history.
- After an SCD Type 2 change, should the already-loaded fact rows be repointed to the new dimension version?No. Those facts happened while the old version was in force, and the whole value of Type 2 is that their key preserves that. Repointing them makes the model behave exactly like Type 1 while paying all of Type 2's storage and complexity costs. The only legitimate restatement is fixing facts that were loaded against a wrong or placeholder version.
- Why does counting rows in a Type 2 dimension overstate the number of customers?Each change adds another row for the same member, so the table holds versions, not members. Count distinct on the durable business key for "how many customers have ever existed", or filter to the current version for "how many customers do we have now". Facts joined to the dimension are unaffected — each fact still matches exactly one version.
saying these in an interview costs you the question
- Type 2 means keeping a separate history table on the side
- Type 1 stashes the previous value somewhere you can query
- Always choose Type 2 because more history is always better
- After a Type 2 change, update old facts to the newest key
- Counting dimension rows to count customers