The Power BI Fellowship
Patterns & Playbooks · Patterns

Semantic modeling pattern library

Fact and dimension patterns with when to use each: transaction, snapshot, accumulating and factless facts, mixed grains, conformed, role-playing, SCD 1 and 2, junk, degenerate, parent-child and bridge dimensions, plus bad vs good model diagrams.

BI DeveloperSenior BI DeveloperBI EngineerBI Architect / LeadPower BI DesktopTabular EditorSQL engineChecked 2 Oct 2026Download .md
On this page (6)

The one rule: decide the grain first

Every fact table has a grain: what one row means ("one order line", "one product per warehouse per week"). Write it down before you add a column. Most modeling bugs (double counting, totals that don't add up, budgets that can't be compared with sales) are grain bugs.

Bad vs good

The flat table

Snowflake and bidirectional chains

Fact patterns

PatternGrainExampleWatch out for
Transaction factOne row per eventFactSales: one order lineDuplicates from re-exports; returns as negative rows or a separate fact
Periodic snapshotOne row per entity per periodFactInventory: stock per product per weekSemi-additive: never sum over time (pattern)
Accumulating snapshotOne row per process instance, updated as it movesOrder lifecycle: ordered, packed, shipped, delivered dates on one rowSeveral date roles; rows change after load
Factless factOne row per occurrence or eligibility, no measureAttendance, "customer eligible for promotion"Count rows; the absence of a row is information
Multiple fact tablesEach its own grainSales and BudgetRelate each to shared (conformed) dimensions, never to each other
Different grainsDaily sales, monthly targetsFactSales vs FactBudgetRelate the coarser fact to a coarser attribute (month, category); blank below its grain

Dimension patterns

PatternWhat it isExampleTeach yourself with
Conformed dimensionOne dimension shared by several facts, with the same keys and meaningOne DimCustomer for Sales, Returns and SupportSprint 05
Role-playing dimensionOne dimension used in several rolesDimDate as Order Date and Ship DateUSERELATIONSHIP, or a second date table if both roles are needed on one visual
SCD type 1Overwrite the attribute; no historyCorrecting a misspelt cityKeys and SCDs
SCD type 2New row per change with valid-from/to and a surrogate key; facts point at the version current when they happenedCustomer moves region: old sales stay in the old regionKeys and SCDs
Junk dimensionSeveral low-cardinality flags combined into one small dimensionIsPromo × IsOnline × PaymentTypeDimension patterns
Degenerate dimensionA dimension attribute with no table, kept on the factOrderID on FactSalesUse it for counts and drill-through; don't build a dimension for it
Parent-childA self-referencing hierarchyDimEmployee[ManagerKey] → EmployeeKeyPATH, PATHITEM to flatten into levels
BridgeResolves many-to-many between a dimension and a fact or two dimensionsAccounts with several owners; products in several promotionsBridge table plus a bidirectional or CROSSFILTER relationship, used deliberately (guidance)

SCD type 2 in practice

sql
-- the dimension keeps every version
-- CustomerSK (surrogate key), CustomerID (business key), Region, ValidFrom, ValidTo, IsCurrent
-- facts store CustomerSK of the version valid on the transaction date
SELECT f.*, d.CustomerSK
FROM staging_sales AS f
JOIN dim_customer AS d
  ON d.CustomerID = f.CustomerID
 AND f.OrderDate >= d.ValidFrom AND f.OrderDate < d.ValidTo;

Decide which attributes are type 2 (tracked) and which are type 1 (overwritten), and record the decision in the model design document. Microsoft documents SCD type 2 loading with Fabric Data Factory; see the references.

Model checklist

The full version is the model review checklist.

Something missing or out of date? Open an issue or edit content/toolkit/guides/modeling-patterns.md.