skip to content

What does an SCD Type 3 previous-value column let a business report?

level: middleimportance: must knowfreq 60%

answer

  1. a column, not a row
  2. two answers from one row
  3. how many old values fit?
  4. think territory redraw, not customer moves
  5. the next change overwrites it

basics

~20 s

An SCD Type 3 column keeps one prior value beside the current one on the same dimension row, so the same measures can be reported under both the old and the new attribute at once. It captures a realignment, not a timeline.

solid answer

~50 s

Type 3 adds a **second column** for the attribute — say `prior_territory` next to `territory` — rather than a second row. When the attribute changes, the current value is copied into the prior column and the new value is written over the current one. The dimension row keeps its surrogate key, so no new version appears and no fact row is affected. Its purpose is a **planned realignment**: sales territories are redrawn, cost centres are reorganised, product hierarchies are restructured, and the business wants to see the same numbers under both maps side by side during the transition. Because a row holds both values, a single query can group by either. The limitation is structural: exactly one prior value survives, the next change overwrites it, and nothing records which value was in force for any particular fact. Type 3 is a comparison device, not a history mechanism.

code

sql · 16 lines
sql
CREATE TABLE dim_sales_rep (
  rep_sk               INTEGER PRIMARY KEY,
  rep_id               VARCHAR(20),
  territory            VARCHAR(40),  -- current
  prior_territory      VARCHAR(40),  -- value before the last realignment
  territory_changed_on DATE
);

-- the same facts, restated under either map, by swapping one column
SELECT d.territory, SUM(f.amount) AS sales
FROM   fact_sales f JOIN dim_sales_rep d ON d.rep_sk = f.rep_sk
GROUP  BY d.territory;

SELECT d.prior_territory, SUM(f.amount) AS sales
FROM   fact_sales f JOIN dim_sales_rep d ON d.rep_sk = f.rep_sk
GROUP  BY d.prior_territory;

go deeper

for a junior

Recall the shape: Type 3 adds a column holding one earlier value on the same row, instead of adding rows. Know that it keeps only one prior value and that the next change overwrites it.

for a middle

Explain what the load does on change — copy current into prior, write the new value, stamp the date — and why a single query can then report the same facts under either column. Be precise that no version and no new key is created.

for a senior

Show when you would reach for it: a planned realignment where the business needs both maps at once, layered on top of whatever the real history strategy is. Name the failure mode of an analyst mistaking the column for history and building a trend on it.

for a principal

Own the naming and documentation contract around alternate-reality columns, and the decision of how many named alternates the published model will support before that requirement is really a versioning requirement in disguise.

## The shape of a Type 3 change Slowly Changing Dimension Type 3 responds to an attribute change by **adding a column, not a row**. The dimension keeps one row per member; alongside the live attribute it carries a companion column holding the value from before the last change. ```sql CREATE TABLE dim_sales_rep ( rep_sk INTEGER PRIMARY KEY, rep_id VARCHAR(20), -- durable business key territory VARCHAR(40), -- current value prior_territory VARCHAR(40), -- value before the last realignment territory_changed_on DATE ); ``` On change, the load copies `territory` into `prior_territory`, writes the new value into `territory`, and stamps the change date. The surrogate key does not move, so every fact row already pointing at this rep continues to point at the same row and silently gains access to both values. ## Why anyone would want this The motivating case is an **organisational realignment**, not the ordinary drift of an attribute. A company redraws its sales territories on 1 January. Every historical sale was made under the old map. The VP of Sales wants two things simultaneously: the current year plan measured under the new territories, and last year's actuals *restated* under those same new territories so the comparison is apples-to-apples — and also the ability to flip back to the old map so the reps recognise their own numbers. Type 2 versioning does not give you that flip. Under Type 2, each fact is welded to the version in force when it happened; "restate all history under the new map" is not a grouping choice, it requires re-deriving keys. Under Type 3, both columns sit on every row, so restating is just choosing a column: ```sql -- last year's actuals under the new territory map SELECT d.territory, SUM(f.amount) FROM fact_sales f JOIN dim_sales_rep d ON d.rep_sk = f.rep_sk GROUP BY d.territory; -- the same actuals under the map the reps worked under SELECT d.prior_territory, SUM(f.amount) FROM fact_sales f JOIN dim_sales_rep d ON d.rep_sk = f.rep_sk GROUP BY d.prior_territory; ``` Both queries scan the same facts and produce different, equally legitimate answers. This "alternate reality" reporting is the entire point of Type 3, and it is the phrase to use in an interview. ## The three hard limits 1. **Fixed depth.** One prior column holds one prior value. You may add `territory_2_ago` if the business genuinely needs two, but the depth is baked into the schema; every extra level of history is a schema change plus a load change. It does not scale, and it is not meant to. 2. **Silent loss on the next change.** The second realignment overwrites the prior column with the first realignment's value. Whatever was there before is gone, exactly as under Type 1. 3. **No per-fact attribution.** The columns say what this member's current and previous values are. They do not say which value was in force on the date of any given fact. A sale from four years ago cannot be attributed to the territory of its own time unless that value happens to still be sitting in the prior column. Those limits are why Type 3 is a *supplement*. A Type 3 column that is asked to carry real history is a defect. ## Variants worth knowing Some designs keep an **original** value rather than the immediately-previous one — `original_territory` frozen at the member's first load, which is effectively a Type 0 column living beside a Type 1 column. Others keep a small fixed set of named alternates (current, prior, budget map) because those specific maps are the ones the business reports against. Both are still Type 3 in spirit: fixed-width alternate values on one row. Type 3 is also one of the three ingredients in the Type 6 hybrid, where it sits beside genuine Type 2 versioning to give the model both an as-was and an as-is view. ## Practical cautions Name the columns so an analyst cannot confuse them — `territory_current` / `territory_prior` beats `territory` / `territory_old`, and document what "prior" is prior *to*. Publish the change date next to the pair; without it, nobody can tell whether the prior value is a month old or four years old. And be honest in the model documentation that the column is a comparison aid: the failure mode is a report author who assumes it means history and quietly builds a trend on it.

  • Why can a Type 3 column not tell you the value in force for a fact from three changes ago?
    Because it stores state, not a timeline. The column always holds the value from immediately before the most recent change, and each change overwrites it. There is no association between a fact's date and either column, so any fact older than the last change may fall under a value neither column still remembers.
  • How does a Type 3 prior-value column differ from the current-value column in a Type 6 dimension?
    They point in opposite directions. A Type 3 column carries an old value forward onto a single, non-versioned row. A Type 6 dimension has genuine Type 2 versions, and its current-value column pushes today's value backwards onto every historical version so old facts can be rolled up under the present-day attribute.
  • When is adding a second and third prior column still a reasonable design?
    When the alternates are few, named and stable — a current map, last year's map, and the budget map, for instance. The business reports against those specific views repeatedly, the count does not grow with time, and each column has a meaning an analyst can state. If the count would grow with change volume, the requirement is Type 2, not more columns.

saying these in an interview costs you the question

  • Type 3 keeps the full history of an attribute
  • Type 3 adds a new row for each change
  • A Type 3 column tells you the value at the time of a fact
  • Type 3 is a cheaper substitute for Type 2
  • Adding a prior column changes the row's surrogate key

context