Microsoft Power BI • Data Modeling

Fact vs. Dimension Tables — Distinguish fact tables vs dimension tables (conceptual)

Understanding the two fundamental table types that form the backbone of every analytical data model.

Microsoft Power BI emblem with a dashboard, stacked chart, and database.
Inside this lesson 10 sections 5 questions 5 figures

SECTION 1

Historical Context & Motivation

The distinction between fact tables and dimension tables did not emerge with Power BI — it is rooted in decades of research on how organizations should structure data for analytical querying. In the 1960s and 1970s, relational database theory focused on online transaction processing (OLTP), where highly normalized schemas minimized redundancy at the expense of query complexity. As businesses accumulated vast repositories of historical data, a new paradigm was needed — one optimized not for writing individual transactions, but for reading and aggregating large volumes of records. This need gave rise to the field of dimensional modeling, which treats data as a combination of measurable events and the descriptive contexts surrounding them.

1970 • Codd's Relational Model

E. F. Codd published "A Relational Model of Data for Large Shared Data Banks," establishing the theoretical foundation for relational databases and normalization — the standard for OLTP systems.

1988 • Kimball's Dimensional Modeling

Ralph Kimball introduced dimensional modeling, proposing that analytical databases should be structured around star schemas composed of fact and dimension tables, optimized for human understandability and query performance.

1996 • The Data Warehouse Toolkit

Kimball's seminal book codified best practices for building dimensional models, establishing fact and dimension tables as the canonical building blocks of data warehouses across industries.

2009 • VertiPaq & Power Pivot

Microsoft introduced the VertiPaq columnar engine in Power Pivot (later integrated into Power BI), bringing dimensional modeling directly to business analysts with in-memory compression and DAX calculations.

2015–Present • Power BI Ecosystem

Power BI Desktop and Service mature into the dominant self-service BI platform, with star schema design — fact and dimension tables connected by relationships — at the core of every performant data model.

The central question that dimensional modeling answers is deceptively simple: how should we organize data so that analytical queries are both fast and intuitive? The answer hinges on cleanly separating quantitative measurements from the descriptive attributes that give those measurements meaning. This separation is the conceptual foundation we will explore throughout this lesson.

SECTION 2

Core Principles & Definitions

At its core, dimensional modeling divides every dataset into two categories of tables. A fact table records measurable events or transactions — the "what happened" of your business. Each row typically represents a single event such as a sale, a shipment, or a web click, and the table's numeric columns (called measures) are the values you aggregate in reports: revenue, quantity, duration. A dimension table, by contrast, stores the descriptive context — the "who, what, where, when, why, and how" that qualifies each event. Dimension tables contain textual or categorical attributes such as product name, customer region, or calendar month, which analysts use to slice, filter, and group the facts.

1

Fact Tables Store Events

Each row in a fact table captures a discrete business event or measurement. Columns are primarily foreign keys (linking to dimensions) and numeric measures (values to aggregate).
2

Dimension Tables Store Context

Dimension tables provide the descriptive attributes — product names, customer demographics, geographic regions — used to filter, group, and label the numeric facts in reports and dashboards.
3

Grain Defines Granularity

The grain of a fact table specifies what a single row represents (e.g., one line item per order). Choosing the correct grain is the most critical design decision in dimensional modeling.
4

Star Schema Topology

In a star schema, a central fact table connects to multiple surrounding dimension tables via one-to-many relationships, forming a star-like topology. Power BI's engine is optimized for this pattern.
✦

KEY TAKEAWAY

Think of a fact table like a log file — it records every event with timestamps and numeric values but minimal human-readable context. Dimension tables are like lookup dictionaries that translate cryptic foreign key IDs into meaningful labels. Just as a compiler resolves symbol references via a symbol table, a BI engine resolves fact-table keys through dimension tables to produce readable, filterable reports.

SECTION 3

Visual Explanation — The Star Schema

Star Schema — Fact Table Surrounded by Dimension TablesFACT: SalesFK: ProductKeyFK: CustomerKeyFK: DateKeyFK: StoreKeyMeasure: RevenueDIM: ProductPK: ProductKeyProductNameCategoryBrandDIM: CustomerPK: CustomerKeyCustomerNameRegionSegmentDIM: DatePK: DateKeyFullDateMonthNameFiscalYearDIM: StorePK: StoreKeyStoreNameCityCountryDashed lines represent one-to-many relationships (dimension → fact)
The central Sales fact table holds foreign keys and measures. Four dimension tables — Product, Customer, Date, and Store — radiate outward, each connected by a one-to-many relationship on their respective primary/foreign key pair. This topology is why the design is called a star schema.

Observe the asymmetry between the two table types. The fact table is typically tall and narrow — it may contain millions or even billions of rows (one per transaction), but its column count is modest: mostly integer foreign keys and a handful of numeric measures. Dimension tables, by contrast, are wide and short — a Product dimension might have only 5,000 rows (one per distinct product) but 30 or more descriptive columns (name, category, subcategory, brand, color, weight, introduction date, and so on). This structural difference directly influences how Power BI's VertiPaq engine compresses and indexes each table, and it is the reason star schemas consistently outperform flat, denormalized tables in analytical workloads.

SECTION 4

How It Works — Relationships & Filter Flow

In Power BI, the relationship between a dimension table and a fact table is always a one-to-many (1:N) relationship where the dimension table sits on the "one" side (each row has a unique primary key) and the fact table sits on the "many" side (its foreign key column may repeat the same dimension key across thousands of rows). This relationship defines the filter propagation direction: by default, filters flow from the dimension side to the fact side. When a user selects "Electronics" in a slicer connected to the Product dimension, that filter propagates through the relationship to the fact table, restricting aggregations to only those rows where ProductKey matches an electronics product.

Filter Propagation in a Star SchemaDIM: ProductCategory = "Electronics"FACT: SalesFiltered rows onlyFILTERVisual: Bar ChartSUM(Revenue)RESULTConceptual Query Execution1. User selects Category = "Electronics" in slicer2. Engine identifies matching ProductKey values in DIM: Product3. Filter propagates to FACT: Sales via FK relationship4. Engine aggregates SUM(Revenue) over filtered fact rows
Filters originate from a dimension table and propagate through the relationship to the fact table. The VertiPaq engine resolves matching keys and then computes the requested aggregation. This unidirectional flow from dimension to fact is the default behavior in Power BI.

This unidirectional filter flow is not merely a UI convenience — it reflects a deep design principle. Because dimension tables have unique keys and fact tables have repeating keys, the engine can efficiently hash-join the filtered dimension keys against the fact table's compressed column segments. Bidirectional filtering (where the fact table can also filter a dimension table) is possible but should be used sparingly, as it can introduce ambiguity, degrade performance, and create circular dependency risks in more complex models.

💡

Cross-Filter Direction

In Power BI's Model view, each relationship has a cross-filter direction property: Single (dimension → fact, the default and recommended setting) or Both (bidirectional). For a clean star schema, keep the default single direction.

SECTION 5

Detailed Breakdown — Fact Types & Dimension Characteristics

Types of Fact Tables

Not all fact tables are created equal. The three canonical types — transaction facts, periodic snapshot facts, and accumulating snapshot facts — capture events at different temporal resolutions. A transaction fact table records each individual event (e.g., every line item in every order). A periodic snapshot captures the state of a measure at regular intervals (e.g., daily inventory levels). An accumulating snapshot tracks the lifecycle of a process across multiple milestones (e.g., an order moving through stages: placed → shipped → delivered → returned).

Fact Table TypeGrainRow GrowthExample
TransactionOne row per eventUnbounded — grows with activityIndividual retail POS line items
Periodic SnapshotOne row per entity per periodPredictable — one row per periodDaily account balances
Accumulating SnapshotOne row per entity lifetimeFixed — rows updated, not appendedOrder fulfillment pipeline

Three canonical fact table types classified by grain and growth pattern

Characteristics of Dimension Tables

Dimension tables share several structural hallmarks. Each has a surrogate key — typically an integer primary key generated during the ETL process to uniquely identify each row. Dimension tables are denormalized: rather than splitting Product and ProductCategory into two normalized tables with a foreign key, you flatten them into one wide table so the BI engine can resolve attributes in a single lookup. This deliberate redundancy trades storage efficiency for query speed — a classic time–space trade-off that should feel familiar from algorithm design.

  • Surrogate key: A synthetic integer primary key, independent of source system identifiers, enabling consistent joins and historical tracking.
  • Natural key: The original business identifier (e.g., SKU or employee ID) retained as an attribute column for reference but not used as the join key.
  • Descriptive attributes: Text or categorical columns (ProductName, Color, Size) used in slicers, axes, and legends of Power BI visuals.
  • Hierarchies: Columns that form a drill-down path (e.g., Year → Quarter → Month → Day in a Date dimension).

SECTION 6

Worked Example — Designing a Star Schema for an E-Commerce Dataset

Suppose you are given a flat CSV export from an e-commerce database containing the columns: OrderID, OrderDate, ProductName, Category, Subcategory, CustomerName, CustomerEmail, City, State, Country, Quantity, UnitPrice, Discount, TotalAmount. Your task is to decompose this into a proper star schema within Power BI.

Decomposing a Flat Table into Fact and Dimension Tables

  1. 1

    Step 1 — Identify the Grain

    The first question to answer: what does one row represent? In this dataset, each row is a single line item within an order. An order may span multiple products, so the grain is one product per order. This becomes the grain of our fact table.
    Grain: one line item per order
  2. 2

    Step 2 — Separate Measures from Attributes

    Scan each column and classify it as either a numeric measure (something you would SUM, AVG, or COUNT) or a descriptive attribute (something you would filter, group, or label by). Quantity, UnitPrice, Discount, and TotalAmount are measures. ProductName, Category, Subcategory, CustomerName, CustomerEmail, City, State, Country, and OrderDate are attributes.
    Measures: Quantity, UnitPrice, Discount, TotalAmount. Attributes: everything else.
  3. 3

    Step 3 — Group Attributes into Dimensions

    Cluster related attributes into logical dimension tables. ProductName, Category, and Subcategory belong to a Product dimension. CustomerName, CustomerEmail, City, State, and Country form a Customer dimension. OrderDate maps to a Date dimension (typically a pre-built calendar table with Year, Quarter, Month, DayOfWeek, and other temporal attributes).
    Three dimensions identified: DimProduct, DimCustomer, DimDate.
  4. 4

    Step 4 — Create Surrogate Keys

    Generate unique integer surrogate keys for each dimension table (ProductKey, CustomerKey, DateKey). In Power Query, you can add an Index Column starting at 1 for Product and Customer after removing duplicates. For the Date dimension, the integer key is commonly the date in YYYYMMDD format (e.g., 20240315).
    Surrogate keys: ProductKey (INT), CustomerKey (INT), DateKey (INT as YYYYMMDD).
  5. 5

    Step 5 — Construct the Fact Table

    The fact table retains only the foreign keys (ProductKey, CustomerKey, DateKey) and the numeric measures (Quantity, UnitPrice, Discount, TotalAmount). All descriptive text columns have been moved to dimension tables. In Power BI's Model view, create one-to-many relationships from each dimension's primary key to the corresponding fact table foreign key.
    Result: FactSales (ProductKey, CustomerKey, DateKey, Quantity, UnitPrice, Discount, TotalAmount) with three relationships to DimProduct, DimCustomer, and DimDate.

SECTION 7

Fact vs. Dimension — Head-to-Head Comparison

CharacteristicFact TableDimension Table
PurposeStores measurable events / metricsStores descriptive context / attributes
Typical Row CountMillions to billionsHundreds to low millions
Typical Column CountNarrow (5–15 columns)Wide (10–50+ columns)
Key Column RoleForeign keys referencing dimensionsPrimary (surrogate) key — unique per row
Data TypesMostly integers, decimals, currencyMostly text, dates, booleans
AggregationColumns are summed, averaged, countedColumns are used to filter, group, slice
NormalizationAlready minimally structuredDeliberately denormalized (flat)
Filter Role in Power BIReceives filters (many side)Originates filters (one side)

Side-by-side comparison of fact and dimension table characteristics

🔑

DESIGN HEURISTIC

If you are unsure whether a column belongs in a fact or a dimension table, apply this test: would you SUM or AVG this column in a report? If yes, it is a measure and belongs in the fact table. Would you put this column in a slicer, legend, or axis label? If yes, it is an attribute and belongs in a dimension table. This heuristic resolves the classification for the vast majority of columns you will encounter.

SECTION 8

Connection to Advanced Theory — Snowflake, Galaxy, and Beyond

The star schema is the simplest and most performant dimensional topology, but it is not the only one. As data models grow in complexity, two extensions arise. The snowflake schema normalizes dimension tables by splitting hierarchical attributes into sub-dimension tables (e.g., a Product dimension linked to a separate Category dimension). While this reduces storage via deduplication, it increases the number of joins required at query time and complicates the model for business users — Power BI's documentation explicitly recommends avoiding snowflaking in most scenarios. The galaxy schema (also called a fact constellation) involves multiple fact tables sharing conformed dimensions, which is essential when modeling complex business domains with several distinct processes (e.g., Sales facts and Inventory facts both referencing the same Product and Date dimensions).

Schema TypeDimension StructureQuery ComplexityPower BI Recommendation
StarFully denormalized dimensionsLow — single hop from fact to dimensionStrongly recommended
SnowflakeNormalized dimensions with sub-dimensionsMedium — multi-hop joins requiredGenerally discouraged; flatten in Power Query
Galaxy / ConstellationShared (conformed) dimensions across multiple factsVaries — depends on number of fact tablesAppropriate for complex multi-process domains

Comparison of dimensional schema topologies

Looking forward, as you progress into advanced data engineering and modeling, you will encounter concepts like slowly changing dimensions (SCDs) — strategies for handling dimension attribute changes over time (Type 1 overwrites, Type 2 adds versioned rows, Type 3 adds previous-value columns). You will also encounter role-playing dimensions where a single physical dimension table (like a Date table) participates in multiple relationships to the same fact table under different roles (OrderDate, ShipDate, DeliveryDate). These advanced patterns all build on the foundational distinction between facts and dimensions that this lesson has established.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

A colleague argues that the Customer table in your Power BI model is a fact table because it contains a "TotalLifetimeSpend" column. Explain why this classification is incorrect, and clarify where TotalLifetimeSpend should reside in a proper star schema.
Show answer
The Customer table is a dimension table because its primary purpose is to provide descriptive context (CustomerName, Region, Segment) and each row uniquely identifies one customer. The presence of a pre-aggregated numeric column like TotalLifetimeSpend does not transform it into a fact table. In a well-designed star schema, TotalLifetimeSpend should not exist as a stored column at all — it should be computed dynamically as a DAX measure (e.g., SUM(FactSales[TotalAmount])) that aggregates from the fact table. Storing pre-aggregated values in a dimension violates the single-source-of-truth principle and can lead to inconsistencies when new transactions are added.

Question 2: BASIC CALCULATION

You have a flat table with 500,000 transaction rows. There are 2,000 unique products, 10,000 unique customers, and 730 unique dates (two years of daily data). After decomposing into a star schema, how many total rows exist across the fact table plus all three dimension tables? What percentage reduction in total cell count does the star schema achieve compared to the flat table if the flat table has 14 columns and the star schema has: FactSales (6 columns), DimProduct (5 columns), DimCustomer (6 columns), DimDate (8 columns)?
Show answer
Total rows in the star schema: 500,000 (fact) + 2,000 (product) + 10,000 (customer) + 730 (date) = 512,730 rows. Total cells in the flat table: 500,000 × 14 = 7,000,000 cells. Total cells in the star schema: (500,000 × 6) + (2,000 × 5) + (10,000 × 6) + (730 × 8) = 3,000,000 + 10,000 + 60,000 + 5,840 = 3,075,840 cells. Percentage reduction: (7,000,000 − 3,075,840) ÷ 7,000,000 × 100 ≈ 56.1%. The star schema eliminates redundant repetition of descriptive attributes across every transaction row, achieving a significant reduction in total data volume while also improving query semantics.

Question 3: INTERMEDIATE

You are building a Power BI model for a hospital. The source data includes a table with columns: AdmissionID, PatientName, PatientDOB, DiagnosisCode, DiagnosisDescription, DoctorName, DoctorSpecialty, AdmissionDate, DischargeDate, RoomNumber, DailyCharge, InsurancePaid, PatientPaid. Design a star schema: identify the fact table (with its grain), the dimension tables, and which columns go where. Explain any design decisions.
Show answer
The grain of the fact table (FactAdmission) is one row per hospital admission. Its columns: AdmissionKey (surrogate), PatientKey (FK), DiagnosisKey (FK), DoctorKey (FK), DateKey_Admission (FK), DateKey_Discharge (FK), RoomNumber, DailyCharge, InsurancePaid, PatientPaid. DimPatient: PatientKey, PatientName, PatientDOB. DimDiagnosis: DiagnosisKey, DiagnosisCode, DiagnosisDescription. DimDoctor: DoctorKey, DoctorName, DoctorSpecialty. DimDate: DateKey, FullDate, Month, Year, DayOfWeek. Note that DimDate is a role-playing dimension used twice — once for AdmissionDate and once for DischargeDate. RoomNumber could be placed in either the fact table (if treated as a degenerate dimension — a descriptor that lives directly in the fact row) or extracted into its own DimRoom table if rooms have additional attributes like floor, wing, or bed count.

Question 4: APPLIED

A data engineering team delivers a Power BI model where a single large table called 'AllData' contains 50 columns and 20 million rows. Reports are running slowly, and the PBIX file is 2.4 GB. Describe the systematic steps you would take to refactor this into a star schema, including specific Power Query transformations, and explain why each step should improve performance.
Show answer
First, identify the grain of AllData (e.g., one row per transaction). Second, classify each of the 50 columns as either a measure or a descriptive attribute using the SUM/filter test. Third, in Power Query, create dimension queries by referencing the AllData query, selecting only the relevant attribute columns, removing duplicates (to get unique entities), and adding an Index column as the surrogate key. Fourth, modify the AllData query (now the fact table) to retain only foreign keys and measures — use Merge Queries to replace text attribute columns with their corresponding surrogate keys. Fifth, in Model view, create one-to-many relationships from each dimension PK to the fact FK. Performance improves for three reasons: (1) VertiPaq compresses integer foreign keys far more efficiently than repeated text strings, reducing the file size dramatically; (2) the engine can hash-join small dimension tables against the large fact table rather than scanning redundant text; (3) filter propagation through relationships is optimized in the storage engine, producing faster DAX query evaluation.

Question 5: CRITICAL THINKING

Consider a scenario where a 'ProductPrice' changes frequently — sometimes multiple times per day. Discuss whether UnitPrice should be stored in the fact table, the Product dimension table, or a separate structure. Analyze the trade-offs of each approach in terms of historical accuracy, model complexity, query performance, and DAX measure authoring.
Show answer
There are three viable approaches, each with distinct trade-offs. Approach A: Store UnitPrice in the fact table as a degenerate measure. This is the simplest and most common solution — each transaction row records the price at the time of the event, preserving perfect historical accuracy without any additional complexity. DAX measures like SUM(FactSales[UnitPrice] × FactSales[Quantity]) are straightforward. The trade-off is that price analysis independent of transactions (e.g., 'what is the current list price?') requires a separate lookup. Approach B: Store UnitPrice in the Product dimension and implement a Type 2 slowly changing dimension (SCD2), creating a new dimension row each time the price changes. This preserves history and keeps the fact table lean, but dramatically increases DimProduct row count, complicates surrogate key management, and makes DAX authoring harder because you must ensure the correct version of the product is joined. Approach C: Create a separate FactPriceChange or bridge table that records each price change event with an effective date range. This is the most normalized but also the most complex to query. For most Power BI models, Approach A — storing the transaction-time price directly in the fact table — is the pragmatic best choice, because it preserves accuracy with minimal complexity. Approach B is justified when non-transactional price analysis is a frequent business requirement. Approach C is rarely worth the complexity in a self-service BI context.

SUMMARY

Lesson Summary

Summary

Every well-designed Power BI data model rests on the fundamental distinction between fact tables and dimension tables. Fact tables are tall, narrow repositories of numeric measures and foreign keys, recording individual business events at a defined grain. Dimension tables are wide, short collections of descriptive attributes — the who, what, where, when, and why — connected to the fact table via one-to-many relationships that form a star schema.

This architecture enables filter propagation from dimensions to facts, allowing users to slice and aggregate data intuitively. Fact tables come in three types — transaction, periodic snapshot, and accumulating snapshot — while dimension tables are characterized by surrogate keys, denormalized structure, and drill-down hierarchies. Mastering this conceptual separation is the gateway to building performant, maintainable models in Power BI and the broader ecosystem of analytical data platforms.

Varsity Tutors • Microsoft Power BI • Fact vs. Dimension Tables