Why does an SCD Type 4 mini-dimension help when a dimension attribute changes weekly?
answer
- Type 2 charges you a row per change
- how many customers versus how many combinations?
- the fact row gains a second key
- continuous values must be banded first
- the base dimension stops growing
basics
~20 sVersioning a weekly-changing attribute would multiply the dimension into tens of versions per member. SCD Type 4 moves those volatile attributes into a small separate table of distinct value combinations that the fact row joins to directly, leaving the base dimension stable.
solid answer
~50 sType 2 versioning charges you a whole dimension row for every change, so an attribute that moves weekly on a large dimension turns a customer table into something the size of a fact table — and every one of those versions repeats all the stable attributes too. **Type 4** splits the problem: the volatile attributes are lifted out into their own small dimension holding one row per *distinct combination* of their values, and the fact table carries a second foreign key pointing at the combination in force when the event happened. Continuous values are banded (age band, income band, score band) so the combination count stays in the hundreds or thousands rather than growing with members or time. The base dimension goes back to changing slowly. The trade-offs are real: history of the volatile attributes now exists only where facts recorded it, so a period with no activity leaves no trace, and analysts must know to join two dimensions.
code
sql · 22 lines-- volatile, banded attributes live in their own bounded table
CREATE TABLE dim_customer_profile (
profile_sk INTEGER PRIMARY KEY,
age_band VARCHAR(10),
income_band VARCHAR(10),
score_band VARCHAR(10),
UNIQUE (age_band, income_band, score_band)
);
-- the fact points at the stable customer AND the state at event time
CREATE TABLE fact_sales (
sale_sk BIGINT PRIMARY KEY,
date_sk INTEGER,
cust_sk INTEGER REFERENCES dim_customer(cust_sk),
profile_sk INTEGER REFERENCES dim_customer_profile(profile_sk),
amount DECIMAL(12,2)
);
-- sales by the income band the buyer was in when they bought
SELECT p.income_band, SUM(f.amount)
FROM fact_sales f JOIN dim_customer_profile p ON p.profile_sk = f.profile_sk
GROUP BY p.income_band;go deeper
Recall the basic shape: fast-changing attributes are pulled out of the big dimension into a small separate table, and the fact row points at both. Know that the reason is avoiding a row per change per member.
Explain why the mini-dimension is bounded — one row per distinct combination of banded values, not per member — and what the fact table gains, namely a second foreign key capturing the state at event time.
Weigh the trade-offs out loud: state is only captured where facts exist, band definitions become a published contract, and analysts can silently join the wrong profile. Say when plain Type 2 is the simpler correct answer instead.
Own the band definitions as a governed vocabulary across teams, and the call about which attributes justify the extra join and the extra concept in a model that many consumers already depend on.
## The problem Type 4 solves Slowly Changing Dimension Type 2 is priced per change: each change to any tracked attribute inserts a new row for that member. That price is fine for attributes that move once every few years — a relocation, a category reassignment. It is ruinous for attributes that move constantly. Take a 40-million-row customer dimension with a behavioural score that is recalculated weekly. Under Type 2 that is up to 52 new rows per customer per year, each one a full copy of the customer's name, address, segment and every other column, and none of the copies differ in anything anyone browses the dimension by. Within two years the "dimension" has more rows than the fact table it decorates, loads take hours, and every distinct-count query is a trap. The insight behind Type 4 is that the volatile attributes are not really describing the same slowly-changing thing. They are describing a **state the member was in at a point in time** — which is much closer to something a fact should reference directly. ## The mini-dimension form The volatile attributes are removed from the base dimension and placed in their own table with one row per **distinct combination** of their values: ```sql CREATE TABLE dim_customer_profile ( profile_sk INTEGER PRIMARY KEY, age_band VARCHAR(10), income_band VARCHAR(10), score_band VARCHAR(10), UNIQUE (age_band, income_band, score_band) ); ``` Crucially the rows are combinations, not members: if there are 8 age bands, 10 income bands and 5 score bands, the table has at most 400 rows *no matter how many customers you have or how often they move between bands*. Movement between states costs nothing, because every state already exists as a row. The fact table then carries two dimension keys — one for the stable customer, one for the profile in force at event time: ```sql CREATE TABLE fact_sales ( sale_sk BIGINT PRIMARY KEY, date_sk INTEGER, cust_sk INTEGER, -- stable customer dimension profile_sk INTEGER, -- mini-dimension state at the time of sale amount DECIMAL(12,2) ); ``` A query can now slice sales by income band at time of purchase without the customer dimension ever having versioned. **Banding is what makes it work.** Raw continuous values — an exact age, an exact score, a balance to the cent — would produce a combination row per distinct value tuple and reintroduce the explosion. Type 4 requires you to agree with the business on bands, which is itself a modelling decision with reporting consequences: the bands become the vocabulary of every future analysis, and changing them later restates history. ## The history-table form The same type number is also used for a second, simpler split: keep only the **current** version of each member in the main dimension and push retired versions into a parallel history table with the same shape. Everyday current-state queries hit a small, fast, one-row-per-member table and need no current-row filter; the rarer as-was analyses join to the history table. It is the same idea — segregate the part that grows from the part that is browsed — applied to whole rows rather than to volatile columns. ## What you give up - **State is only recorded where facts exist.** The mini-dimension key is stamped onto facts. If a customer's band changes during a quiet month with no transactions, nothing in the model remembers it. If the business needs a continuous record of band membership, you need a periodic snapshot fact, not a mini-dimension. - **Two joins, and a rule.** "Income band" is now reachable two ways in a naive design, and analysts must know they should use the profile attached to the fact, not the customer's current profile. Some designs additionally carry the *current* profile key on the base dimension row for current-state browsing; that convenience is exactly where analysts confuse as-was with as-is, so it must be named clearly. - **The bands are a commitment.** They are visible in every report, and redefining them is a breaking change to a published model. ## Deciding when to reach for it The honest signals are: the attribute changes on a cadence measured in days or weeks; the dimension is large; the attribute is analytical rather than identifying (nobody looks a customer up by it); and its values either are, or can be, a small closed set. When those hold, Type 4 converts an unbounded row explosion into a bounded lookup. When they do not — the attribute changes twice a year, or the dimension has fifty thousand rows — plain Type 2 is simpler and the explosion never materialises. Reaching for a mini-dimension on a small, stable dimension is over-engineering, and interviewers notice.
- Why must continuous values be banded before they go into a mini-dimension?The table holds one row per distinct combination, so raw continuous values would produce a combination for nearly every tuple observed and reintroduce exactly the explosion the split was meant to prevent. Banding fixes the number of rows at the product of the band counts, independent of member count and of time. The cost is that the bands become the reporting vocabulary and are expensive to redefine later.
- After moving an attribute into a mini-dimension, how do you answer "what band was this customer in last March?" if they made no purchases?You cannot, from that model alone. The mini-dimension key is only recorded where a fact exists, so a quiet period leaves no trace of band membership. If continuous coverage is a requirement, add a periodic snapshot fact that records each customer's profile key per period regardless of activity — that is the structure designed to answer status-over-time questions.
- What is the risk of also storing the current profile key on the base customer dimension row?It is convenient for current-state browsing but creates two paths to the same attribute with different meanings. An analyst who joins the customer's current profile instead of the fact's profile gets today's band applied to years of history and will not see an error, only different numbers. If you offer it, name the columns so the distinction is unmissable and document which one answers which question.
saying these in an interview costs you the question
- Type 4 keeps a mini-dimension row per customer
- Move the volatile attribute out but keep raw continuous values
- The mini-dimension gives a complete history of band membership
- Use a mini-dimension for any attribute that ever changes
- The fact table needs no extra key after the split