Power BI Data Modeling Deep Dive

Star Schema vs Snowflake Schema — Which to Use in Power BI?

Your Power BI report loads in 12 seconds. Your DAX measures return confusing results. Your model view looks like a plate of spaghetti. In most cases, the root cause is the same — a poorly designed data model.

The schema you choose — Star or Snowflake — is the single most impactful decision you make before writing a single DAX measure. This guide explains both schemas clearly, compares their trade-offs, and gives you a definitive answer on which to use in Power BI.

📋
Who This Is For

This guide is for Power BI developers, data analysts, and BI architects who design data models — whether in Power BI Desktop, Azure Synapse, SQL Server, or any other platform that feeds into Power BI.

1. Why Data Modeling Matters in Power BI

Power BI's analytical engine — VertiPaq — is a columnar, in-memory engine that compresses and stores your data. It is extraordinarily fast when the data model is structured correctly. It becomes slow and error-prone when it is not.

A good data model:

  • Makes DAX simpler — measures become 2 lines instead of 20
  • Makes reports faster — VertiPaq works best with specific relationship patterns
  • Makes maintenance easier — adding a new dimension doesn't break existing measures
  • Makes slicers work correctly — filters propagate predictably across tables

The two dominant schema patterns in dimensional modelling are the Star Schema and the Snowflake Schema. Understanding both — and when to use each — is a core skill for any Power BI developer.

2. Key Concepts — Fact Tables and Dimension Tables

Before comparing schemas, it helps to be clear on the two building blocks:

Table TypeWhat It ContainsExamples
Fact TableMeasurable, numeric business events — the data you aggregateFactSales, FactInventory, FactProduction
Dimension TableDescriptive attributes that give context to factsDimProduct, DimCustomer, DimDate, DimRegion

A fact table typically has millions of rows and relatively few columns — mostly foreign keys and numeric measures. A dimension table has fewer rows but many descriptive columns. The relationship between them drives every filter and calculation in your report.

3. Star Schema

In a Star Schema, each dimension table connects directly to the central fact table. There is exactly one hop between any dimension and the fact — the model diagram looks like a star with the fact table at the centre.

Star Schema — Table Structure
-- FACT TABLE (centre of the star)
FactSales
├── SaleID          (PK)
├── DateKey         (FK → DimDate)
├── ProductKey      (FK → DimProduct)
├── CustomerKey     (FK → DimCustomer)
├── RegionKey       (FK → DimRegion)
├── SalesPersonKey  (FK → DimSalesPerson)
├── Quantity
├── UnitPrice
└── Revenue

-- DIMENSION TABLES (points of the star) — ALL denormalised
DimProduct
├── ProductKey (PK)
├── ProductName
├── Category          ← stored directly in DimProduct
├── SubCategory       ← stored directly in DimProduct
├── Brand             ← stored directly in DimProduct
└── UnitCost

DimDate
├── DateKey (PK)
├── Date, Year, Quarter, Month, Week, Weekday
└── IsHoliday, FiscalYear, FiscalQuarter

DimCustomer
├── CustomerKey (PK)
├── CustomerName, Email, Phone
├── City, State, Country  ← all geography in one table
└── Segment, Tier

Key characteristic: Dimension tables are denormalised — all attributes for a subject (product, customer, geography) are stored in a single flat table. This means some data is repeated (e.g., "Electronics" appears in thousands of rows), but that repetition is intentional and beneficial in VertiPaq because of its columnar compression.

4. Snowflake Schema

In a Snowflake Schema, dimension tables are normalised — broken into smaller sub-dimension tables. Instead of one DimProduct table containing Category and SubCategory, those become separate tables linked to DimProduct.

Snowflake Schema — Table Structure
-- FACT TABLE (same as before)
FactSales
├── SaleID
├── DateKey         (FK → DimDate)
├── ProductKey      (FK → DimProduct)
├── CustomerKey     (FK → DimCustomer)
├── Quantity, UnitPrice, Revenue

-- NORMALISED DIMENSION CHAIN (snowflake "branches")
DimProduct
├── ProductKey (PK)
├── ProductName
├── SubCategoryKey  (FK → DimSubCategory)  ← linked, not stored
└── UnitCost

DimSubCategory                             ← separate table
├── SubCategoryKey (PK)
├── SubCategoryName
└── CategoryKey    (FK → DimCategory)      ← another level up

DimCategory                                ← another separate table
├── CategoryKey (PK)
└── CategoryName

-- Geography also split into multiple tables
DimCustomer
├── CustomerKey (PK)
├── CustomerName, Email
└── CityKey     (FK → DimCity)

DimCity → DimState → DimCountry            ← chain of 3 tables

Key characteristic: Data is normalised — no redundancy, smaller storage footprint. However, every filter on Category must now travel through multiple table relationships to reach the fact table.

5. Side-by-Side Comparison

AttributeStar SchemaSnowflake Schema
StructureFact + flat dimension tablesFact + normalised dimension hierarchy
Number of TablesFewer (1 dim per subject)More (multiple dims per subject)
Data RedundancyHigher — values repeated in dimsLower — each value stored once
Storage SizeSlightly larger dim tablesSmaller dim tables
Query Speed⚡ Faster — fewer joins🐢 Slower — more joins required
DAX ComplexitySimpler — direct filter propagationComplex — multi-hop filter chains
VertiPaq Fit✅ Ideal⚠️ Works but not optimal
MaintenanceEasier for BI/reportingEasier for OLTP/transactional systems
Power BI RelationshipsSimple one-to-manyChains of one-to-many
Used ByPower BI, Tableau, Analysis ServicesData Warehouses (Snowflake, Redshift)

6. Performance in Power BI — Why Star Schema Wins

Power BI's VertiPaq engine is optimised for the Star Schema pattern. Here is why:

Filter Propagation

When a user selects "Electronics" from a slicer:

  • Star Schema: Filter hits DimProduct → travels one relationship → filters FactSales. One hop.
  • Snowflake Schema: Filter hits DimCategory → travels to DimSubCategory → travels to DimProduct → travels to FactSales. Three hops.

Each additional relationship hop adds latency. On large datasets (10M+ rows), this difference is measurable in seconds.

DAX Measure Complexity

Consider a simple measure — Revenue for a selected Category:

DAX — Star Schema (simple)
-- Star Schema: Category is directly on DimProduct
-- No extra DAX needed — slicer on DimProduct[Category] just works

Revenue by Category =
[Total Revenue]
-- That's it. Filter context handles everything automatically.
DAX — Snowflake Schema (complex)
-- Snowflake: Category is in a separate DimCategory table
-- Need CROSSFILTER or TREATAS to make the filter reach FactSales

Revenue by Category (Snowflake) =
CALCULATE(
    [Total Revenue],
    CROSSFILTER(DimSubCategory[CategoryKey], DimCategory[CategoryKey], BOTH)
)
-- More complex, harder to debug, easier to get wrong

VertiPaq Compression

VertiPaq compresses columns using dictionary encoding and run-length encoding. A denormalised DimProduct with "Electronics" repeated 500 times compresses extremely well — often better than you'd expect. The storage savings from normalisation (Snowflake) are largely negated by VertiPaq's compression, removing one of the few advantages Snowflake has in traditional databases.

7. When to Use Star Schema

Use Star Schema in Power BI in almost every situation. Specifically:

  • You are building reports and dashboards for business users
  • Performance is a priority (it almost always is)
  • Your dimension hierarchies have 2–4 levels (Category → SubCategory → Product)
  • You want simple, maintainable DAX measures
  • Your dataset fits in Power BI's in-memory model (under ~1 billion rows)
  • You are using Import Mode or DirectQuery with a well-designed warehouse
💡
Flatten Your Snowflake in Power Query

If your data warehouse uses a Snowflake Schema (which many do), flatten it in Power Query before it reaches Power BI. Merge DimSubCategory and DimCategory into DimProduct using Table.NestedJoin or the Power Query Merge Queries UI. Present a Star Schema to Power BI even if the source is a Snowflake.

8. When Snowflake Schema Makes Sense

Snowflake Schema is appropriate in specific scenarios — mostly outside of Power BI's in-memory model:

  • Data Warehouse layer: When designing tables in Snowflake, Redshift, BigQuery, or Azure Synapse for storage efficiency and ETL flexibility
  • DirectQuery on very large tables: When the database engine handles the joins and VertiPaq is not involved
  • Rapidly changing hierarchies: When category structures change frequently and you want to update one table instead of denormalising again
  • Shared dimension layers: When multiple fact tables share the same sub-dimension (e.g., DimGeography shared across Sales, HR, and Logistics)
⚠️
Snowflake Schema in Import Mode — Avoid It

In Power BI Import Mode, a Snowflake Schema gives you the worst of both worlds — larger model size (due to relationship overhead), slower DAX, and more complex debugging. Always flatten to Star Schema before importing.

9. Real-World Example — Sales Data Model

Here is a production Star Schema for a manufacturing company's sales reporting in Power BI:

Production Star Schema — Sales Analytics
── FACT TABLE ────────────────────────────────────────────
FactSales (15 million rows)
├── SaleKey         BIGINT  (PK)
├── DateKey         INT     (FK → DimDate)
├── ProductKey      INT     (FK → DimProduct)
├── CustomerKey     INT     (FK → DimCustomer)
├── SalesPersonKey  INT     (FK → DimSalesPerson)
├── Quantity        DECIMAL
├── UnitPrice       DECIMAL
├── Discount        DECIMAL
└── Revenue         DECIMAL  (Quantity * UnitPrice * (1 - Discount))

── DIMENSION TABLES ──────────────────────────────────────
DimDate (3,650 rows — 10 years of dates)
├── DateKey, Date, DayName, DayOfWeek
├── Week, WeekStart, Month, MonthName, MonthShort
├── Quarter, Year, FiscalYear, FiscalQuarter
└── IsHoliday, IsWeekend, IsBusinessDay

DimProduct (500 rows — fully denormalised)
├── ProductKey, ProductCode, ProductName
├── Category, SubCategory, Brand          ← no separate tables needed
├── UnitCost, PackSize, IsActive
└── LaunchDate, DiscontinuedDate

DimCustomer (8,000 rows)
├── CustomerKey, CustomerCode, CustomerName
├── Email, Phone, ContactPerson
├── City, State, Region, Country          ← geography flattened in
├── Segment, Tier, AcquisitionDate
└── IsActive, CreditLimit

DimSalesPerson (45 rows)
├── SalesPersonKey, EmployeeCode, Name
├── Team, Manager, Region
└── JoinDate, TargetRevenue

── DISCONNECTED TABLES (slicers only — no relationships) ─
SlicerTopN    → {5, 10, 20, 50}
SlicerPeriod  → {"MTD", "QTD", "YTD", "Last 12 Months"}

Notice how geography (City, State, Region, Country) lives directly inside DimCustomer rather than in a separate DimGeography table. Category and SubCategory live directly in DimProduct. This is a Star Schema — flat, simple, and fast.

10. How to Flatten a Snowflake in Power Query

If your source database uses Snowflake Schema, here is how to flatten it into a Star Schema inside Power Query before the data reaches Power BI's model:

Power Query M — Flatten Snowflake to Star
// Start with DimProduct (has SubCategoryKey but no Category name)
let
    Source         = Sql.Database("server", "database"),

    DimProduct     = Source{[Schema="dbo", Item="DimProduct"]}[Data],
    DimSubCategory = Source{[Schema="dbo", Item="DimSubCategory"]}[Data],
    DimCategory    = Source{[Schema="dbo", Item="DimCategory"]}[Data],

    // Step 1: Merge SubCategory into Product
    MergeSubCat = Table.NestedJoin(
        DimProduct, "SubCategoryKey",
        DimSubCategory, "SubCategoryKey",
        "SubCatData", JoinKind.LeftOuter
    ),
    ExpandSubCat = Table.ExpandTableColumn(
        MergeSubCat, "SubCatData",
        {"SubCategoryName", "CategoryKey"}
    ),

    // Step 2: Merge Category into the result
    MergeCat = Table.NestedJoin(
        ExpandSubCat, "CategoryKey",
        DimCategory, "CategoryKey",
        "CatData", JoinKind.LeftOuter
    ),
    ExpandCat = Table.ExpandTableColumn(
        MergeCat, "CatData",
        {"CategoryName"}
    ),

    // Step 3: Remove the FK columns — we only need the names
    FinalProduct = Table.RemoveColumns(
        ExpandCat, {"SubCategoryKey", "CategoryKey"}
    )

in
    FinalProduct

// Result: DimProduct now has ProductName, SubCategoryName,
// CategoryName all in one flat table — Star Schema ready.

11. Data Modeling Best Practices for Power BI

  • Always use a dedicated Date table — create one with CALENDAR() or CALENDARAUTO() and mark it as a Date Table. Never use dates directly from the fact table.
  • Use integer surrogate keys — integer relationships are faster than string relationships. Use INT or BIGINT keys, not GUIDs or natural keys.
  • Keep fact tables narrow — only foreign keys and measurable numeric columns. Move descriptive text to dimension tables.
  • Avoid bidirectional relationships — they cause ambiguous filter propagation and slow performance. Use CROSSFILTER() in DAX when you need it for specific measures instead.
  • Hide foreign key columns — hide all FK columns (ProductKey, DateKey, etc.) from the report view. Users should filter on dimension attributes, not keys.
  • Use disconnected tables for slicers — parameter slicers (Top N, Period selectors) should use unrelated tables with no relationships.
  • Prefix your tables — use Fact/Dim/Slicer prefixes so the model view stays organised as it grows.

12. Decision Guide — Which Schema to Choose?

Your SituationRecommended Schema
Building a Power BI report or dashboard✅ Star Schema
Designing a SQL data warehouse for BI✅ Star Schema
Source data comes from a normalised ERP/CRM✅ Flatten to Star in Power Query
Dimensions have 2–4 hierarchy levels✅ Flatten into one dimension table
Hierarchy changes frequently (e.g., org chart)⚠️ Snowflake acceptable in warehouse layer
DirectQuery on Snowflake / BigQuery warehouse⚠️ Snowflake acceptable — DB engine handles joins
Multiple fact tables sharing geography data⚠️ Snowflake in warehouse, flatten per fact in PQ
Import Mode with in-memory model❌ Never Snowflake — always flatten
🎯
The Short Answer

For Power BI — always Star Schema. If your source is a Snowflake, flatten it in Power Query before it reaches the model. The performance difference is real, the DAX simplicity is significant, and Microsoft's own best practice documentation recommends Star Schema for Power BI in every scenario.


Ankit Kumar

Power BI Developer · Data Modeler · etlguru

Ankit Kumar is a Data Analyst and founder of Pyivot Solutions with 5+ years of experience designing Star Schema data models, building Power BI dashboards, and delivering BI solutions for manufacturing, trading, and e-commerce businesses across India.