Grain is the answer to: "What does one row represent?"
Examples:
- One row per order
- One row per order line
- One row per customer per day
- One row per page view event
If the grain is unclear, joins and aggregates silently double-count.
fct_orders grain: 1 row per order join to order_items (1 row per line) without aggregating first -> revenue duplicated across lines
How to protect grain
- Write the grain in one sentence in the model docs
- Enforce uniqueness tests on the grain key
- Aggregate to the needed grain before joining facts to facts
- Never mix two grains in one fact table
Tiny wrong vs right
Wrong: "fct_sales has some header rows and some line rows." Right: "fct_order_headers is one row per order; fct_order_items is one row per line."
Interview tip
Start every modeling answer with grain. Senior interviewers listen for that habit.