What You'll Master Here
A fact table is keys + measures at one grain. Every design choice is about keeping a measure true at that grain, and keeping the evidence for it.
Chapter 8 established what a fact table is. This chapter is about the decisions made inside one: which measures earn a column, what an empty measure means, which identifiers get a dimension table, how a header charge reaches line grain, and what happens when a row arrives late or has to be corrected.
Every column below was an argument somebody had. Open each one to see the decision behind it, what it makes answerable, and the bug that ships when it goes the other way.
A fact table is keys + measures at one grain. Every design choice is about keeping a measure true at that grain, and keeping the evidence for it.
Facts hold almost all the data volume and every number anyone reports. Fact-design mistakes are the silent kind: the query runs, returns a number, and the number is quietly wrong at scale.
- measure
- A numeric value in a fact that you aggregate (amount, quantity, balance).
- additivity
- Across which dimensions a measure can be safely summed (additive / semi / non).
- degenerate dimension
- A dimension key (like a bill number) stored on the fact with no dimension table.
- restatement
- A correction to a fact value that has already been reported.
-- GRAIN: one row per vending machine per trading day.
create table fact_vending_day (
machine_key bigint not null references dim_machine(machine_key),
date_key int not null references dim_date(date_key),
machine_serial text not null, -- id only: no dimension table
units_sold int not null, -- additive
stock_on_hand int not null, -- semi-additive: not across days
fault_minutes int, -- null = no telemetry, 0 = no fault
takings_local numeric(12,2) not null,
fx_rate numeric(10,6) not null,
takings_gbp numeric(12,2) not null,
primary key (machine_key, date_key)
);Treating every numeric column as freely summable. You sum balances over time or sum ratios and report nonsense; additivity has to be settled per measure, per axis.
Classify every measure's additivity before exposing it.
Decide what an empty measure means before the first load, not after the first dispute.
Keep the evidence: source amounts, rates, and versions, not just the derived number.
Nearly every bug in this chapter is the same shape: a number that is true at one grain, or in one currency, or at one point in time, being used as though it were true everywhere.
Fact design is a sequence of decisions inside one table; each one keeps a measure true at the declared grain, and each has a signature failure when it is decided the other way.
