Kimball dimensional modelling: the star schema, explained by what it costs
Also called: star schema, dimensional modelling, fact and dimension tables, the Kimball method
The most widely used way to model a warehouse, and the one most often got wrong in the first week — because two of its decisions are made early and cannot be cheaply undone.
What it is
A Kimball model is built out of two kinds of table. A fact table holds the things you measure: an order line, a payment, a meter reading, a support ticket. A dimension table holds the things you slice by: customer, product, date, store, employee. Draw one fact with its dimensions around it and you get the shape everybody calls a star schema.
What makes it the default is not elegance. It is that every business intelligence tool in existence — Power BI, Tableau, Looker, Excel’s own pivot tables — expects this shape and is slower, or simply worse, on anything else. Choosing Kimball means the tooling is on your side.
Ralph Kimball’s own framing is worth keeping: the warehouse is modelled around business processes, not around departments and not around source systems. One star per process. Sales is a process. Returns is another. The marketing department is not a process, and a star built for a department is a star that has to be rebuilt when the org chart moves.
How the tables are actually shaped
The grain: the decision everything else rests on
The grain of a fact table is the answer to what does one row mean? — and it has to be a sentence, not a shrug. “One row is one line of one order” is a grain. “Sales data” is not.
It is decided first because everything downstream assumes it. Which dimensions can attach, which measures can be added up, whether a count means orders or items — all of it follows from the grain. Declare it in writing before a single column is created, and put it in a comment at the top of the table.
The fact table
Narrow and long. Foreign keys to its dimensions, the numbers being measured, and as little else as possible:
FactSales
SalesKey BIGINT IDENTITY -- surrogate, not the source's key
DateKey INT FK -> DimDate
CustomerKey INT FK -> DimCustomer
ProductKey INT FK -> DimProduct
OrderNumber NVARCHAR -- a degenerate dimension: no table of its own
Quantity INT -- additive
LineTotal DECIMAL -- additive
UnitPrice DECIMAL -- NOT additive: summing these means nothing
- Additive measures can be summed across every dimension. Quantity, revenue, cost.
- Semi-additive measures sum across everything except time. An account balance: adding January’s to February’s is nonsense, but adding two accounts’ balances is fine.
- Non-additive measures cannot be summed at all. A unit price, a percentage, a ratio. Average them, or store the numerator and denominator separately and divide at the end.
The dimension table, and how much history it keeps
Wide and short, and deliberately denormalised: a product dimension carries its subcategory and category as columns rather than as joins to two more tables. Normalising them back out is called snowflaking, and it makes every report pay for the tidiness.
The second unavoidable decision is what happens when an attribute changes — a customer moves house. That is the slowly changing dimension question, and in practice it has two answers worth having:
- Type 1 overwrites. The old value is gone. Right when only the current value ever matters, and when keeping the old one would be noise.
- Type 2 adds a new row and closes the old one, usually with
ValidFrom,ValidToandIsCurrentcolumns. History survives, so a report about last year keeps saying what it said last year. This is the one people mean when they say “SCD”. - Types 3 and 6 exist — a previous-value column, and a hybrid of 1 and 2 — and are far rarer than the literature suggests. Reach for them only when you can say out loud which question they answer.
Surrogate keys, and the row that catches the unknown
Every dimension gets a meaningless integer key of its own rather than reusing the source system’s. Two reasons that matter: type 2 history needs several rows for one customer, so the source’s key is no longer unique; and when a second source system arrives with its own customer IDs, the warehouse already has a key that belongs to neither.
Give every dimension an “unknown” row at key -1. A fact whose customer cannot be resolved then points at that instead of being dropped, and the row is still counted in the total — which is how you find out the lookup is broken, rather than quietly reporting a smaller number.
When it fits, and when it does not
Kimball is a good default and a bad universal. It is the right answer surprisingly often and the wrong one in ways that only show up months in.
It fits when
- the business processes are understood and the grain can be agreed
- reporting and dashboards are the main purpose
- one or two source systems, changing slowly
- the team wants something analysts can query without help
It fits badly when
- sources change shape often - every change reaches the star
- many systems must be integrated before anyone agrees on meaning
- full history of every attribute is a hard requirement
- no one can yet say what one row of the main table means
Those two lists are Netune’s own judgement of this methodology, copied from the rule file it scores a real database with.
What it costs
| Cost | What that means here | |
|---|---|---|
| Build | Moderate | One star is a week or two of real work. The modelling questions take longer than the SQL. |
| Maintain | Low | A stable source means a stable star. New attributes are new columns. |
| Query | Very easy | One join per dimension, and every BI tool already understands the shape. |
| History | Per dimension, decided up front | Type 2 where the past must stay right. Retrofitting it later means reloading. |
The mistakes that are expensive
Getting the grain wrong, then building on it
A fact table built at order level when the questions are about products cannot answer them, and no amount of clever SQL recovers the detail that was never stored. Going the other way — a grain finer than anything asked for — costs storage and is otherwise harmless, so when in doubt, go finer.
The tell is a fact table nobody can describe in one sentence. If two people give two different answers to “what is one row?”, stop building.
Letting the star snowflake
Source systems are normalised, so a straight copy gives you Product → Subcategory → Category as three tables, and reports start joining dimension to dimension. The whole point of the star is that they never have to. Flatten the hierarchy into the child dimension — ProductCategoryName as a column on DimProduct — and accept the repetition.
The exception is a parent that a fact or a bridge points at directly. That one is used at its own grain and keeps its table, even while its attributes are also folded into the children.
Keeping type 2 history of somebody else’s housekeeping
This one is quiet and expensive. If a type 2 dimension watches every column for changes, and the source carries a ModifiedDate or a rowversion, then every batch that touches the row opens a new version of it. After a year the dimension holds a detailed history of the source system’s maintenance schedule and almost nothing about the business.
So the change test has to be restricted to the attributes that actually describe the business. Netune marks those columns tracked and writes the others into the load anyway — they are stored, just not watched — and names the ones it ignored in a comment, so the decision is visible rather than mysterious.
How it compares
Kimball or Galaxy?
There is no real choice here; Galaxy is what Kimball becomes when you model a second process. One fact table is a star, several sharing the same dimensions is a constellation. The work it adds is organisational rather than technical: every process has to agree on what a customer is.
Kimball or Data Vault?
Count your source systems, and ask whether you must be able to prove what arrived. One system and reporting questions: Kimball, and you will be finished far sooner. Several systems that disagree, schemas that keep changing, or an audit requirement: Data Vault 2.0 — and then a star on top of it anyway, because nobody reports off a raw vault.
Kimball or One Big Table?
One Big Table is a star with the joins already done, which makes it faster to query and impossible to correct in one place. For a single process on a columnar engine it is a reasonable trade. For several processes sharing customers and products it is a trap: the same customer’s details end up in three wide tables that disagree.
Netune profiles a SQL Server database, classifies each table as a fact, a dimension or a bridge from what is really in it, and — if a star is what the data supports — generates the whole thing: the dimensions with the SCD type each one argues for, the facts at a stated grain, the surrogate keys, the unknown rows, and runnable T-SQL for all of it. It shows the reasoning for every call so you can disagree with it.
Questions people ask
What is the grain of a fact table?
What one row means, stated as a sentence: “one row is one line of one order”. It is decided before anything else because every dimension, every measure and every count depends on it. A fact table nobody can describe in one sentence does not yet have a grain.
What is the difference between SCD type 1 and type 2?
Type 1 overwrites the old value, so history is lost and the dimension stays one row per thing. Type 2 closes the old row and inserts a new one with validity dates, so a report about last March still shows the address the customer had in March. Use type 2 where the past must stay right; type 1 where only today’s value ever matters.
Why use surrogate keys instead of the source system's IDs?
Because type 2 history means one customer has several rows, so the source key stops being unique — and because a second source system arrives with its own IDs for the same customers. A meaningless integer key belongs to the warehouse and survives both.
Is a star schema still relevant with a modern lakehouse?
Yes, and the two are not alternatives. Medallion says where a table sits in the pipeline; Kimball says what a row means. Most lakehouse teams end up modelling the gold layer dimensionally, which is doing both.
How many dimensions is too many?
There is no hard limit, but past a dozen on one fact it is worth checking whether some are really attributes of others — a “dimension” with three columns that only ever appears beside another one usually belongs inside it.
The other seven
- InmonAn integrated 3NF core, with marts built on top
- Data Vault 2.0Hubs, links and satellites; nothing is ever updated
- MedallionBronze, silver, gold — a pipeline, not a model
- GalaxySeveral stars sharing conformed dimensions
- One Big TableEverything joined in already; no joins to write
- Anchor ModelingOne table per attribute; change never disturbs anything
- Activity SchemaOne row per thing that happened, in sequence
Deciding between them
See whether your database wants a star
Netune reads a SQL Server database, works out which tables behave like facts and which like dimensions, and generates the star with the history decisions argued for rather than assumed. Free, runs on your machine, and nothing it reads leaves it.
Download the free beta