DEV Community

Jack Kisutsa
Jack Kisutsa

Posted on

Power BI Data Modelling, Relationships and Joins

Power BI Data Modelling, Relationships and Joins: A Practical Guide

When I began learning Power BI, I naturally focused on dashboards. However, an attractive dashboard can still be slow or misleading when the data behind it is poorly organised. The data model is the foundation: it determines how tables communicate, how filters move and how DAX measures behave.

This article uses a retail-sales example to explain modelling schemas, fact and dimension tables, relationships, filter direction and Power Query joins. It also addresses a common beginner question: if both joins and relationships connect tables, when should each be used?

1. Data modelling in Power BI

Data modelling is the process of organising data into tables and defining logical connections between them. Sales transactions may be stored separately from customers, products, dates and locations. A good model allows a user to select Electronics, Nairobi or 2026 and receive the correct result.

A well-designed model improves accuracy, makes DAX easier to write, reduces duplicated data, improves VertiPaq compression and supports future growth. It also makes the solution easier for another analyst to understand and maintain.

2. Flat table, star schema and snowflake schema

A. Flat table

A flat table stores facts and descriptions together. One Sales table might contain OrderID, Date, CustomerName, ProductName, Category, City, Quantity and SalesAmount.

Advantages: It is quick to build, familiar to Excel users and suitable for a small prototype. No relationships need to be managed.

Disadvantages: Product and customer descriptions repeat for every transaction, increasing table width and storage. Repetition creates inconsistent values and difficult maintenance. Large flat tables are less reusable and can make complex DAX harder to organise.

Best use: Small, one-purpose datasets with limited growth.

B. Star schema

A star schema places a fact table at the centre and connects it directly to dimensions such as Customer, Product, Date and Location.

Advantages: It provides clear filter paths, simpler DAX, less duplication and strong performance. Dimensions can also be reused by several reports.

Disadvantages: It requires deliberate preparation and a clearly defined fact-table grain. Dimensions may contain controlled repetition, such as Category repeated for products in that category.

Best use: Most production Power BI models, especially when data volume and reporting needs will grow.

C. Snowflake schema

A snowflake schema normalises dimensions. Instead of storing Category and Subcategory in DimProduct, it may use DimProduct → DimSubcategory → DimCategory.

Advantages: It reduces repetition within dimensions and can mirror a normalised source database or independently managed hierarchy.

Disadvantages: Extra tables and relationships increase model complexity. Filters cross more paths, fields are harder to find and DAX becomes less intuitive.

Best use: Large or independently governed hierarchies. For ordinary reporting, small snowflaked dimensions are often flattened in Power Query.

Feature Flat table Star schema Snowflake schema
Structure One wide table Central fact with dimensions Dimensions split further
Redundancy High Low and controlled Lowest
Performance at scale Often weak Usually strongest More traversal required
Complexity Simple initially Clear and balanced Highest

3. Fact tables, dimension tables and grain

A fact table records measurable business events—what happened and how much. FactSales may contain DateKey, CustomerID, ProductID, LocationID, OrderID, Quantity, Cost and SalesAmount. It is usually tall and narrow because transactions grow continuously.

A dimension table provides context used for filtering, grouping and labelling. DimProduct may contain ProductName, Brand and Category; DimCustomer may contain CustomerName and Segment; DimDate may contain Month, Quarter and Year. Dimensions answer who, what, when and where, while facts answer how many and how much.

The grain states exactly what one fact row represents. If FactSales has a grain of one product line per order, an order containing three products creates three rows. Grain must remain consistent. Combining order totals with order-line records can double-count revenue.

Table Typical contents
FactSales Foreign keys, Quantity, Discount, Cost, SalesAmount
FactOrders One row per order and order-level measures
FactTransactions Debit, Credit, Amount and account keys
DimCustomer Customer name, segment, city
DimProduct Product name, brand, category
DimDate Date, month, quarter, year
DimLocation Branch, city, region, country

For example, Total Sales = SUM(FactSales[SalesAmount]) can be analysed by any related dimension without copying its descriptive columns into FactSales.

4. Relationships in Power BI

A relationship is a logical connection between columns in two loaded tables. It allows filters and calculations to work across tables without physically combining them.

A primary key uniquely identifies a dimension row. DimCustomer[CustomerID] should contain C001 once.
A foreign key is the matching fact column; FactSales[CustomerID] may contain C001 many times because that customer can purchase repeatedly. Related columns need compatible data types.

Referential integrity means each foreign key has a matching dimension key. If FactSales contains C999 but DimCustomer does not, Power BI may group the transaction under a blank member. Anti joins can help detect such orphan records.

  • One-to-many (1:*)
    One unique dimension value matches many fact rows. One product appears once in DimProduct but can appear in thousands of sales. This is the standard star-schema relationship.

  • One-to-one (1:1)
    Each key occurs once in both tables—for example, one Employee row matched to one EmployeeSecurity row. It can separate sensitive fields, although merging the tables may be simpler when both describe the same entity at the same grain.

  • Many-to-many (:)
    Keys repeat on both sides. Many students take many courses. A direct many-to-many relationship can make totals difficult to interpret, so a bridge table containing unique StudentID–CourseID pairs is often safer. It connects to Students and Courses through two one-to-many relationships.

An active relationship appears as a solid line and operates automatically. An inactive relationship is dotted and must be activated in a measure. If FactSales has OrderDateKey and ShipDateKey linked to DimDate, Order Date may be active while Ship Date is inactive:

Sales by Ship Date =
CALCULATE(
    [Total Sales],
    USERELATIONSHIP(FactSales[ShipDateKey], DimDate[DateKey])
)
Enter fullscreen mode Exit fullscreen mode

5. Filter direction

With single-direction filtering, a filter normally travels from the one-side dimension to the many-side fact. Selecting Electronics in DimProduct identifies the relevant ProductIDs, filters FactSales and recalculates Total Sales. This behaviour is predictable and is the recommended default.

Bidirectional filtering allows filters to travel both ways. It can help in carefully designed bridge-table scenarios, but it should not be enabled simply to make a visual work. Multiple routes between tables can create ambiguous filter paths, unexpected totals, more query work and difficult debugging.

6. Joins in Power Query

A join matches rows from two tables using a common column. In Power Query, select Home → Merge Queries, choose both tables and matching columns, choose a join kind then expand the required columns.

Customers (left)

CustomerID Name
C1 Amina
C2 Brian
C3 Carol

Orders (right)

OrderID CustomerID Amount
O10 C1 500
O11 C1 300
O12 C2 700
O13 C4 200

C3 has no order, while O13 refers to a missing customer. This reveals how each join treats unmatched records.

Left outer join

Keeps every customer and matching orders. Amina appears twice; Carol remains with null order values.

CustomerID Name OrderID Amount
C1 Amina O10 500
C1 Amina O11 300
C2 Brian O12 700
C3 Carol null null

Use it when Customers is the master list and no customer should disappear.

Right outer join

Keeps every order and matching customers. O13 remains with a null customer name.

CustomerID Name OrderID Amount
C1 Amina O10 500
C1 Amina O11 300
C2 Brian O12 700
C4 null O13 200

Use it when every transaction must remain. Reversing the tables and using Left Outer gives the same logic.

Full outer join

Keeps all matches and unmatched rows from both tables. The output contains the three valid order matches, C3 with null order fields and O13/C4 with a null name. It is useful for reconciliation, although it may create many nulls.

Inner join

Keeps only keys found in both tables: C1–O10, C1–O11 and C2–O12. C3 and O13 are removed. Use it when analysis requires valid matches only, but check row counts because unmatched data is silently excluded.

Left anti join

Keeps only left-table rows without a right-table match. The result is C3–Carol. It can identify customers who have never purchased, missing submissions or unused master records.

Right anti join

Keeps only right-table rows without a left-table match. The result is O13–C4–200. It is valuable for identifying orphan transactions or broken foreign keys.

7. Power Query merge versus model relationship

Question Power Query merge Model relationship
Stage Data preparation before loading Model view after loading
Effect Physically adds matching data to a query Logically connects separate tables
Purpose Enrichment, consolidation, reconciliation Filtering and analysis
Structure Can create a wider table Preserves facts and dimensions

Suppose FactSales has one million rows and DimProduct contains ProductName and Category. Merging repeats those descriptions across many transactions. A relationship stores each product once and uses ProductID to filter FactSales. This is generally cleaner and more efficient.

A merge is appropriate when creating a dimension from several sources, adding a small lookup required before loading, consolidating datasets or using anti joins for quality checks. Excessive merging can recreate a wide flat table and weaken the model.

8. Recommended model

For a typical BI project, I would use a star schema. I would define the grain first, create dimensions with unique keys and keep transactional measures in fact tables. Relationships would normally be one-to-many with single-direction filtering from dimensions to facts.

I would use inactive relationships for alternative dates, bridge tables for genuine many-to-many scenarios and bidirectional filtering only when the business requirement and path are clear. Power Query merges would prepare data rather than flatten the entire model.

This design improves compression, query performance, DAX simplicity, readability, scalability and maintenance. Most importantly, every table has a clear responsibility. Dashboards are what users see but a trustworthy model is what makes their answers correct.

Top comments (1)

Collapse
 
hellen_atieno_92d1bd37d6d profile image
Hellen A.

Great explanation!