A fact table stores one row per measurement event, and its grain states what one row means: "one row per order line" is a grain, "sales data" is not. Choose the most atomic grain the source offers: lines can always be summed into orders, never the reverse. Kimball names three kinds of fact table, plus the factless fact table that records an event with no measure (a customer viewed a book page):
| Type | One row per | BookNest example |
|---|---|---|
| Transaction | Event, when it happens | Order line sold |
| Periodic snapshot | Entity per period | Customers on file per quarter |
| Accumulating snapshot | Process instance, updated as it moves | Order from paid to delivered |
The classic grain mistake is mixing levels. A coupon discount belongs to the whole order: copied onto every line, sum(discount) counts it once per line. Allocate it to lines in proportion to gross, or keep it in an order-grain fact table, as mart.sales did by leaving the net total on orders.