Warehouse Modeling
Model so each fact answers one question at one grain. Star schemas, activity schemas, and wide marts are shapes; grain is the contract.
For pipeline mechanics (extract, load, replay, DAG), use etl-pipeline-design. For KPI numerator/denominator, use metrics-definitions. For writing a query, use sql-analytics. For producer-consumer event contracts, use data-contract-design.
Workflow
- Name the business process and the questions the table must answer.
- Write grain as one sentence: "one row per ___". If two grains appear, split into two facts — refuse to mix them.
- Choose shape (star, activity schema, or wide mart) and say why.
- Facts: measures, additivity, degenerate keys.
- Dimensions: natural keys, surrogates, attributes, conformed reuse.
- SCD per changing attribute (0/1/2/3). Default Type 2 when history matters.
- Grain tests: uniqueness, no fan-out vs source, measure reconcile.
- Late-arriving facts/dims and delete/restate policy.
Output format
## Warehouse model: <process>
**Grain:** one row per …
**Shape:** star | activity schema | wide mart
**Why this shape:** …
### Facts
| Table | Grain | Measures | Additivity | Degenerate keys |
### Dimensions
| Table | Natural key | Surrogate | Conformed with | Attributes |
### SCD
| Dimension / attr | Type | Effective dating | Current flag |
### Grain tests
- uniqueness of the business key at grain
- no fan-out vs source
- measures reconcile to source at that grain
### Open questions
…
Rules
- Always write grain as "one row per <entity> at <event or period>". Two grains in one fact is a defect — split header vs line, event vs daily rollup, snapshot vs transaction.
- Never invent warehouse vendor features (clustering syntax, search optimization, dynamic tables, undocumented MERGE flags). If the user named a platform, use only features they stated or mark assumptions.
- Facts hold measures at grain; dimensions hold descriptive context. A wide mart is allowed only at one consumer grain, named explicitly.
- Conformed dimensions share keys and meaning across facts. Reuse date/customer/product; do not fork
customer_for_orderswithout why. - SCD Type 2 needs effective_from/to (or equivalent) and a current flag. Type 1 overwrites history — say what reports will get wrong.
- Activity schema: one row per activity occurrence (name + timestamp + join keys). Do not mix activity rows with rolled-up daily facts.
- Additive measures sum across all dims; semi-additive (balances) do not sum over time; non-additive (ratios) must be recomputed.
- Do not write the ETL DAG here (
etl-pipeline-design). Do not define KPI formulas here (metrics-definitions).
Good: fct_order_line — one row per order_line_id; fct_order — one row per order_id; shared dim_customer, dim_date. Bad: one fct_orders with both order_total and line_qty.
Edge cases
- Order header + line: two facts sharing conformed dims. Never one fact with both order_total and line_qty.
- Changing plan/segment: Type 2 if reports need the attribute as-of the fact; Type 1 only if overwrite is accepted.
- Activity vs star: activity schema when many event types share keys and analysts want sequences; star when a few processes have stable measures.
- Wide mart: one row per customer-day (or stated grain) is fine. Document column explosion; still one grain.
- Missing source grain: ask; do not invent a primary key.