DEV Community

Cover image for Data Modelling, Relationships and Joins - PowerBI
Bradley Okello
Bradley Okello

Posted on

Data Modelling, Relationships and Joins - PowerBI

Introduction

A Power BI report is only as good as the data model behind it. DAX calculations, visual interactions, refresh performance, and long-term maintainability all depend on how tables are structured, related, and filtered. This article explains data modelling concepts in Power BI, compares flat, star, and snowflake schemas, explains fact and dimension tables, covers relationship cardinalities and filter direction, demonstrates Power Query joins, and recommends a practical model design for real-world BI projects.

1. Data Modelling in Power BI

Data modelling in Power BI means designing the tables, columns, relationships, hierarchies, and measures that form the analytical layer of a report. It is not simply loading data and dragging fields onto a canvas. A good model decides:

  • Which tables should exist.
  • What grain each table represents.
  • Which columns are keys, attributes, or measures.
  • How tables relate to one another.
  • How filters should flow.
  • Which calculations belong in DAX versus Power Query.

A well-designed model matters because it directly affects:

  • Reporting - Users can slice data intuitively and consistently.
  • Analytics - Business questions are answered with correct aggregations.
  • DAX - Measures are simpler and less error-prone.
  • Performance - Fewer relationships, smaller tables, and efficient scans improve speed.
  • Scalability - New facts and dimensions can be added without redesign. Maintainability Logic is centralised and easier to audit.

1.1 Flat Table

Definition

A flat table stores all data in one wide table. Each row contains both descriptive attributes and numeric measures.

Structure

All columns - customer name, product name, date, category, quantity, sales amount - live in the same table.

FlatSales

OrderDate CustomerName ProductName Category Quantity SalesAmount
2026-01-02 Alice Bike Bikes 1 500
2026-01-02 Alice Helmet Accessory 2 80
2026-01-03 Ben Bike Bikes 1 700

Advantages

  • Simple to create and understand.
  • Fast for very small datasets or quick prototypes.
  • No relationship design required.

Disadvantages

  • Heavy redundancy: customer and product details repeat on every row.
  • Large file size and slower refresh as data grows.
  • Difficult DAX: distinct counts and semi-additive measures become complex.
  • Poor scalability and maintainability.
  • Filtering is limited because there are no separate dimension tables.

When Appropriate

  • Very small personal reports.
  • One-off analysis where the data will not grow.
  • Initial data exploration before building a proper model.

Power BI Implications

Power BI can query a flat table, but the model will not take advantage of columnar compression as effectively as a star schema. Report performance and DAX simplicity usually suffer.

1.2 Star Schema

Definition

A star schema consists of one or more central fact tables surrounded by dimension tables. It is the most common and recommended schema for Power BI.

Structure

The fact table stores business events and numeric measures. Dimension tables store descriptive attributes and connect to the fact table through keys.

Sales Schema Diagram

erDiagram
    DimDate ||--o{ FactSales : "1:*"
    DimCustomer ||--o{ FactSales : "1:*"
    DimProduct ||--o{ FactSales : "1:*"
    DimLocation ||--o{ FactSales : "1:*"

Example tables:

FactSales

DateKey CustomerID ProductID LocationID Quantity SalesAmount
20260102 1 100 10 1 500
20260102 1 101 10 2 80
20260103 2 100 20 1 700

DimCustomer

CustomerID CustomerName City
1 Alice Nairobi
2 Ben Mombasa

DimProduct

ProductID ProductName Category
100 Bike Bikes
101 Helmet Accessory

Advantages

  • Excellent Power BI performance due to columnar compression and simple relationships.
  • Simple DAX: measures aggregate the fact table and filters flow from dimensions.
  • Intuitive for report authors and business users.
  • Scalable: new facts and dimensions can be added with minimal disruption.
  • Supports conformed dimensions shared across multiple fact tables.

Disadvantages

  • Requires ETL work to separate facts and dimensions.
  • Some redundancy remains in dimension tables, which is usually acceptable.
  • Poorly designed dimensions can still cause performance issues.

When Appropriate

  • Most business intelligence projects.
  • Any model with multiple business questions, multiple facts, or growth expectations.
  • Models using DAX heavily.

Power BI Implications

Power BI’s VertiPaq engine is optimised for star schemas. Relationships are simple, filter propagation is predictable, and DAX is easier to write and tune.

1.3 Snowflake Schema

Definition

A snowflake schema is a star schema where dimension tables are normalised into additional related tables.

Structure

Instead of one wide DimProduct, product category and department are stored in separate tables.

Product Hierarchy Snowflake Schema

erDiagram
    FactSales }o--|| DimProduct : "*:1"
    DimProduct }o--|| DimCategory : "*:1"
    DimCategory }o--|| DimDepartment : "*:1"

Example:

DimProduct

ProductID ProductName CategoryID
100 Bike 1
101 Helmet 2

DimCategory

CategoryID CategoryName DepartmentID
1 Bikes 10
2 Accessories 20

DimDepartment

DepartmentID DepartmentName
10 Outdoor
20 Safety

Advantages

  • Reduces redundancy in dimension data.
  • Can simplify governance when dimensions are very large and shared.
  • Useful when source systems are already normalised.

Disadvantages

  • More tables and relationships increase model complexity.
  • DAX may require more relationship navigation.
  • Performance can degrade if too many small joins are introduced.
  • Report authors may find it harder to locate fields.

When Appropriate

  • Very large dimensions where normalisation saves significant space.
  • Enterprise models with shared conformed dimensions.
  • Source systems that already provide normalised dimension tables.

Power BI Implications

Power BI can handle snowflake schemas, but the star schema is generally preferred. Snowflake dimensions should be used selectively, and the reporting layer should still feel like a star.

2. Fact Tables and Dimension Tables

Fact Tables

Fact tables store numeric business events and foreign keys. They are usually tall and narrow.

Typical fact table columns:

  • Foreign keys to dimensions: DateKey, CustomerID, ProductID, LocationID.

  • Degenerate dimensions: OrderNumber, InvoiceNumber.

  • Measures: Quantity, SalesAmount, CostAmount, DiscountAmount.

Examples:

  • FactSales
  • FactOrders
  • FactTransactions
  • FactInventory

Dimension Tables

Dimension tables store descriptive attributes used for slicing and grouping. They are usually shorter and wider.

Typical dimension table columns:

  • Primary key: CustomerID, ProductID, DateKey.
  • Descriptive attributes: CustomerName, City, Country, ProductName, Category, Year, Month.

Examples:

  • DimCustomer
  • DimProduct
  • DimDate
  • DimLocation
  • DimEmployee

Measures vs Attributes

A measure is a numeric value that can be aggregated, such as SalesAmount. An attribute is a descriptive field used for filtering or grouping, such as ProductCategory or CustomerCity.

Grain of a Fact Table

The grain defines what one row in the fact table represents. For example:

FactSales grain: one row per product per sales order line.

If a row represents an entire order, then quantity and sales amount cannot be analysed by product. Grain must be defined before modelling because it determines which dimensions can relate to the fact table.

Practical Star Schema Example

A retail company wants to analyse sales by date, customer, product, and location.

Sales Star Schema Diagram

erDiagram
    DimCustomer ||--o{ FactSales : "connects to"
    DimProduct ||--o{ FactSales : "connects to"
    DimDate ||--o{ FactSales : "connects to"
    DimLocation ||--o{ FactSales : "connects to"

FactSales:

  • DateKey
  • CustomerID
  • ProductID
  • LocationID
  • Quantity
  • SalesAmount

DimDate:

  • DateKey
  • Date
  • Year
  • Quarter
  • Month

DimCustomer:

  • CustomerID
  • CustomerName
  • City
  • Country

DimProduct:

  • ProductID
  • ProductName
  • Category
  • Subcategory

DimLocation:

  • LocationID
  • StoreName
  • Region
  • Country

This design allows a user to slice SalesAmount by Year, Customer City, Product Category, or Store Region with simple, fast DAX.

3. Relationships in Power BI

A relationship in Power BI connects two tables using common columns, usually a primary key in one table and a foreign key in another. Relationships are necessary because data is distributed across multiple tables. They allow filters to propagate and enable DAX to aggregate related data correctly.

Relationship Cardinalities

One-to-Many (1:*)

This is the most common relationship.

How it works:

  • One row in the dimension table relates to many rows in the fact table.
  • The dimension side contains unique values.
  • The fact side can contain repeated values.

Example:

  • DimCustomer[CustomerID] 1 → * FactSales[CustomerID]
  • CustomerID is unique in DimCustomer but appears many times in FactSales.

Customer to Sales Relationship

erDiagram
    DimCustomer ||--o{ FactSales : "1:*"

    DimCustomer {
        int CustomerID PK
    }
    FactSales {
        int CustomerID FK
    }

When to use:

  • Almost always between dimensions and facts.
  • It is the foundation of a star schema.

When not to use:

  • Avoid if the “one” side is not unique.
  • Avoid if the relationship creates ambiguous filter paths.

One-to-One (1:1)

How it works:

  • One row in each table relates to exactly one row in the other.
  • Both sides contain unique values.

Example:

DimEmployee[EmployeeID] 1 ↔ 1 DimEmployeeDetails[EmployeeID]

Employee 1-to-1 Relationship

erDiagram
    DimEmployee ||--|| DimEmployeeDetails : "1:1"

When to use:

  • Separating optional or sensitive attributes.
  • Merging two tables with the same grain when a relationship is preferred over a merge.

When not to use:

  • If the tables can be merged cleanly.
  • If it adds complexity without analytical benefit.

Many-to-Many (:)

How it works:

  • Many rows in one table relate to many rows in another.
  • Usually implemented through a bridge table.

Example:

  • Students and Courses. A student takes many courses, and a course has many students.

Student-Course Many-to-Many Relationship

erDiagram
    DimStudent ||--o{ BridgeEnrollment : "1:*"
    DimCourse ||--o{ BridgeEnrollment : "1:*"

When to use:

  • True many-to-many business relationships.
  • Bridge tables with measures or weighting factors.

When not to use:

  • Avoid direct many-to-many relationships unless necessary.
  • They can produce unexpected results and complicated DAX.

Key Concepts

  • Primary Key - Unique identifier in a dimension table, e.g. DimCustomer[CustomerID].
  • Foreign Key -Column in a fact table that references a dimension, e.g. FactSales[CustomerID].
  • Unique Values - Required on the “one” side of a 1:* relationship.
  • Cardinality - The number of rows that can relate: 1:*, 1:1, :.
  • Referential Integrity - Every foreign key value should exist in the related primary key table. If not, Power BI may show blank rows.
  • Active Relationship - The default relationship used for filter propagation and DAX.
  • Inactive Relationship - A relationship that exists but is not active by default. Used with USERELATIONSHIP in DAX.

Example: CustomerID contains unique values in DimCustomer because each customer appears once. It appears multiple times in FactSales because one customer can place many orders. This is a classic 1:* relationship.

Active and inactive relationships are common with dates. FactSales may have OrderDateKey, ShipDateKey, and DeliveryDateKey. Only one can be active at a time. A measure can use USERELATIONSHIP to activate another date relationship for specific calculations.

4. Filter Direction

Filter direction determines how filters move between related tables.

Single-Direction Filtering

Filters flow from the “one” side to the “many” side.

Example:

  • DimProduct[Category] filters FactSales.

If a user selects Category = Bikes, Power BI filters DimProduct to Bikes, and that filter propagates to FactSales, showing only bike sales.

Product to Sales Relationship (Single Filter Direction)

erDiagram
    DimProduct ||--o{ FactSales : "Filters ──>"

Single-direction filtering is the default and recommended approach for most star schemas.

Both / Bidirectional Filtering

Filters can flow in both directions.

Example:

  • DimProduct filters FactSales, and FactSales can filter DimProduct.

Bidirectional filtering is sometimes used for:

  • Many-to-many bridge tables.
  • Dimension-to-dimension filtering.
  • Certain DAX patterns.

However, it should be used carefully because it can cause:

  • Ambiguous filter paths.
  • Unexpected results.
  • Slower performance.
  • Circular relationship issues.
  • Unnecessary model complexity.

Example of ambiguity: If DimCustomer and DimProduct both filter FactSales, and FactSales filters both back, Power BI may not know which path should influence the other. This can produce incorrect totals or require complex DAX overrides.

Best practice: Use single-direction filtering by default. Use bidirectional only when a specific analytical requirement cannot be met otherwise, and document it clearly.

5. Joins in Power Query

A join combines rows from two tables based on matching column values. In Power BI, joins are performed in Power Query using Merge Queries.

Example tables:

Customers

CustID Name City
1 Alice Nairobi
2 Ben Mombasa
3 Carol Kisumu
4 David Eldoret

Orders

OrderID CustID Amount
101 1 500
102 1 300
103 2 700
104 4 200
105 5 900

Left Outer Join

How it works:

  • Returns all rows from the left table.
  • Returns matching rows from the right table.
  • Unmatched right columns become null.

Records retained:

  • All Customers.
  • Matching Orders.

Expected output:

Inner / Left Join Result

CustID Name OrderID Amount
1 Alice 101 500
1 Alice 102 300
2 Ben 103 700
3 Carol null null
4 David 104 200

Order 105 is excluded because there is no matching customer.

Right Outer Join

How it works:

  • Returns all rows from the right table.
  • Returns matching rows from the left table.
  • Unmatched left columns become null.

Records retained:

  • All Orders.
  • Matching Customers.

Expected output:

Right Join Result

CustID Name OrderID Amount
1 Alice 101 500
1 Alice 102 300
2 Ben 103 700
4 David 104 200
null null 105 900

Carol is excluded because she has no order.

Full Outer Join

How it works:

  • Returns all rows from both tables.
  • Matches where possible.
  • Unmatched columns become null.

Records retained:

  • All Customers and all Orders.

Expected output:

Full Outer Join Result

CustID Name OrderID Amount
1 Alice 101 500
1 Alice 102 300
2 Ben 103 700
3 Carol null null
4 David 104 200
null null 105 900

Inner Join

How it works:

  • Returns only rows that match in both tables.

Records retained:

  • Only matching Customers and Orders.

Expected output:

Inner Join Result

CustID Name OrderID Amount
1 Alice 101 500
1 Alice 102 300
2 Ben 103 700
4 David 104 200

Carol and Order 105 are excluded.

Left Anti Join

How it works:

  • Returns rows from the left table that have no match in the right table.

Records retained:

  • Customers without Orders.

Expected output:

Unmatched Customers (Left Anti Join Result)

CustID Name City
3 Carol Kisumu

Right Anti Join

How it works:

  • Returns rows from the right table that have no match in the left table.

Records retained:

  • Orders without Customers.

Expected output:

Orphan Orders (Right Anti Join Result)

OrderID CustID Amount
105 5 900

Power Query Illustration

1. Left Outer Join

Keeps all customers; unmatched customers get null order details.

ID Name OrderID CustID
1 Alice 101 1
1 Alice 102 1
2 Ben 103 2
3 Carol null null
4 David 104 4

2. Right Outer Join

Keeps all orders; orphan orders get null customer details.

ID Name OrderID CustID
1 Alice 101 1
1 Alice 102 1
2 Ben 103 2
4 David 104 4
null null 105 5

3. Full Outer Join

Keeps everything from both tables, filling in nulls where there are no matches.

ID Name OrderID CustID
1 Alice 101 1
1 Alice 102 1
2 Ben 103 2
3 Carol null null
4 David 104 4
null null 105 5

4. Inner Join

Only returns exact matches between both tables.

ID Name OrderID CustID
1 Alice 101 1
1 Alice 102 1
2 Ben 103 2
4 David 104 4

5. Left Anti Join

Isolates customers who have never placed an order.

ID Name
3 Carol

6. Right Anti Join

Isolates orphan orders that don't match any customer.

OrderID CustID
105 5

In Power Query: Home → Merge Queries → select matching columns → choose join kind → expand the new table column.

6. Power Query Joins vs Power BI Relationships

Aspect Power Query Merge Power BI Relationship
Stage Before data is loaded After data is loaded, in the model
Effect Physically combines columns/rows into a new query Creates metadata linking tables
Grain Can change or duplicate rows Preserves table grain
Storage Creates wider tables Keeps tables separate
Filtering Not a model filter; it is a transformation Enables filter propagation
DAX Not directly used by DAX relationships Essential for DAX and visuals
Use case Cleaning, enriching, lookup, creating bridge tables Analytical modelling and slicing
Risk Excessive merging creates wide, redundant models Excessive relationships can create ambiguity

A Power Query merge physically combines columns from tables. A Power BI relationship does not combine tables; it tells the model how tables are related.

You would choose a merge when:

  • You need to add lookup columns during ETL.
  • You need to create a bridge table.
  • You need to flatten a small dimension for a specific report.
  • You need to perform anti-join validation.

You would choose a relationship when:

  • You want filter propagation.
  • You want to keep fact and dimension tables separate.
  • You want simple DAX and good performance.
  • You want a reusable star schema.

Excessive merging can destroy a good model. It creates wide tables, duplicates data, increases refresh time, and makes DAX harder. Keeping fact and dimension tables separate is usually preferable because it supports a clean star schema, improves compression, and simplifies reporting.

7. Recommended Power BI Model

For a typical business intelligence project, I recommend a star schema with conformed dimensions and single-direction relationships.

Sales Star Schema Diagram

erDiagram
    DimCustomer ||--o{ FactSales : "filters"
    DimProduct ||--o{ FactSales : "filters"
    DimDate ||--o{ FactSales : "filters"
    DimLocation ||--o{ FactSales : "filters"

Relationships:

  • DimDate[DateKey] 1 → * FactSales[DateKey]
  • DimCustomer[CustomerID] 1 → * FactSales[CustomerID]
  • DimProduct[ProductID] 1 → * FactSales[ProductID]
  • DimLocation[LocationID] 1 → * FactSales[LocationID]

Filter direction:

  • Single-direction from each dimension to the fact table.
  • Avoid bidirectional filtering unless a specific many-to-many requirement demands it.

Additional practices:

  • Mark DimDate as a date table.
  • Hide primary and foreign keys from report view.
  • Use explicit measures instead of implicit aggregations.
  • Create bridge tables for many-to-many relationships.
  • Use inactive relationships and USERELATIONSHIP for role-playing dimensions such as Order Date and Ship Date.
  • Use Power Query merges in staging queries, not as a replacement for the model.

Justification

Factor Star Schema Benefit
Query and report performance Columnar compression and simple relationships scan faster.
DAX simplicity Measures aggregate a single fact table with predictable filters.
Model readability Fact and dimension roles are obvious.
Scalability New facts and dimensions can be added easily.
Data redundancy Dimension attributes are stored once per dimension.
Maintainability ETL and model logic are easier to audit.
Ease of reporting Users see familiar business entities.
Filter propagation Single-direction filters are predictable.
Model complexity Fewer relationships than snowflake, simpler than flat.

A flat table is acceptable only for very small or temporary solutions. A snowflake schema may be useful when dimensions are extremely large or already normalised, but it should be used selectively. The star schema remains the best balance of performance, simplicity, and scalability for Power BI.

Conclusion

Data modelling, relationships, and joins are not isolated technical topics. They are the foundation of a reliable Power BI solution. A flat table is easy but does not scale. A star schema is the standard because it balances performance, DAX simplicity, and maintainability. A snowflake schema can reduce redundancy but adds complexity. Fact tables store measurable events at a defined grain, while dimension tables provide descriptive context. Relationships connect these tables, with 1:* single-direction filtering being the default best practice. Power Query joins are transformation tools, while Power BI relationships are analytical tools. By choosing a star schema, keeping facts and dimensions separate, and using relationships carefully, you create a model that is faster, clearer, and easier to maintain.

Top comments (0)