2.1 · Relationships & data model

Level 2 · Model & DAX

2.1Relationships & data model

Goal: Create and verify relationships in the Model view, and recognise for life the symptom of a missing relationship.
Estimated time: 18 min · self-paced reading · If you only have 5 min: read “The essentials in 30s” + the diagram.

The essentials in 30 seconds

  • Relationships link two tables on a common column (Products[Code] ↔ Sales[ProductCode]): the model propagates the filters, with no formula at all.
  • Normal cardinality: one-to-many (1 → *), from the reference table (the "1" side) to the facts (the "*" side); the single filter direction is the healthy norm.
  • Diagnosis No. 1: identical totals on every row of a visual = missing or broken relationship — always check auto-detected relationships.

Running mini-case — Zineb links her sales to the product reference table

Zineb, a management controller in an agri-food SME in Algiers, needs to cross her sales by product category, but her Sales and Products tables each live on their own. She applies the lesson: in the Model view, she drags Sales[ProductCode] onto Products[Code] — the line appears —, reads the 1:* cardinality and the single filter direction, then proves with a bar chart Category × Amount that the two tables talk to each other. To see the symptom, she deletes the relationship: every total becomes identical across each category. She recreates it at once and remembers diagnosis No. 1 forever.

Standalone case for this section: it returns in the course, the diagram, the script and the 48h challenge of section 2.1.

1. Linking the tables

Decisive level: the model. Your real data arrives in several tables (sales, products, customers) that Power BI links with relationships: a line between two tables on a common column. Once the relationship is set, a visual crosses "Product name" and "Sales amount" with no formula at all — the model propagates the filters. Zineb thus replaces her old VLOOKUPs: the filter flows from one table to the other on its own.

2. Cardinality & direction

We read the cardinality (one-to-many in the vast majority of cases: ONE product ↔ SEVERAL sales) and the filter direction (from the "1" side to the "*" side). Single direction is the healthy norm; bidirectional creates ambiguity and slowness. Auto-detected relationships on import must always be verified in the Model view. Zineb double-clicks the line to read 1:* and "single" before concluding.

3. Diagnosis No. 1

The classic symptom of a missing or broken relationship: identical totals on every row of a visual — each category shows the same total. You recognise it for life. Another cause of a 1:* relationship failing, on the "1" side: duplicates or blanks in the key column, because a dimension has a single row per element. Zineb then fixes it in Power Query (Remove duplicates, filter nulls).

Common misinterpretation: "A relationship is optional: my visuals work anyway, bidirectional mode is more practical, and my key can contain duplicates". No, triple mistake. Without a relationship, the tables do not talk to each other: the totals repeat on every row — that is diagnosis No. 1. The bidirectional direction is almost never needed and creates ambiguity and slowness: the reflex is the single direction (1 → *). And the key column on the dimension side must be unique, with no duplicate or blank, otherwise the 1:* relationship is refused.
A relationship, the channel of filters The filter flows from the dimension (1 side) to the facts (* side). Dimension (1 side)Products[Code] — one row per product+Facts (* side)Sales[ProductCode] — several rows+Filter direction1 → * (single, the healthy norm) Identical totals everywhere = missing relationship (diagnosis No. 1).
The filter flows from the dimension (1 side) to the facts (* side).
Text version of the diagram (SEO / screen readers / translation): 1) Zineb, a management controller in Algiers, links her sales to her product reference table: in the Model view, she drags Sales[ProductCode] onto Products[Code] and the line appears — the model propagates the filters, with no formula. 2) She reads the cardinality (1:* — one product, several sales) and the single filter direction, from the "1" side to the "*" side, and always checks auto-detected relationships. 3) She learns diagnosis No. 1: identical totals on every row of a visual signal a missing or broken relationship — often a key with duplicates or blanks on the dimension side. Lesson: linking tables replaces VLOOKUPs, we keep the single direction, and we check the key on the dimension side.

🔎 Concrete example — 🔎 Concrete example — Sales ↔ Products

Zineb crosses category and amount, two linked tables:

Relationship: Sales[ProductCode] → Products[Code]  (1:*)
→ visual Category (from Products) × Amount (from Sales)
   the two tables talk to each other, with no formula.
Delete the relationship → identical totals everywhere (the symptom).

Reading: the 1:* cardinality is carried by the unique key on the dimension side (Products[Code]).

Lesson: deleting the relationship makes identical totals appear — the diagnostic reflex to recognise.

Section vocabulary

Relationship
The link between two tables on a common column (Products[Code] ↔ Sales[ProductCode]): the channel through which filters flow.

Simple example: Zineb links Sales and Products to cross category and amount.

What it is not: two tables placed side by side, with no line between them.

Reflex: two tables to cross → a relationship on the key column.

One-to-many cardinality (1:*)
The standard case: ONE product of the reference table ↔ SEVERAL sales rows.

Simple example: Zineb reads 1:* on the line between Products and Sales.

What it is not: one-to-one or many-to-many, reserved for other cases.

Reflex: 1:* from the "1" side (dimension) to the "*" side (facts).

Filter direction
By default, the filter goes from the "1" side to the "*" side: filtering a product filters its sales.

Simple example: Zineb keeps the single direction, with no ambiguity.

What it is not: bidirectional mode, a source of slowness and bugs.

Reflex: single direction by default; bidirectional = very special case.

Auto-detection
Power BI guesses some relationships on import: to be checked systematically.

Simple example: Zineb checks every auto-detected relationship in the Model view.

What it is not: a relationship assumed to be good without verification.

Reflex: auto-detected relationship → always check cardinality and direction.

Key column
The common column that carries the relationship: unique on the dimension side, repeated on the facts side.

Simple example: Zineb makes sure Products[Code] is unique.

What it is not: a key with duplicates or blanks that blocks the 1:*.

Reflex: unique key on the "1" side: clean in Power Query if needed.

In practice — In practice — Linking sales and the product reference table

  1. Import a second table "Products" (Code, Name, Category, UnitPrice) — create it in Excel if needed, like Zineb.
  2. Model view: if Power BI has not created the relationship, drag Sales[ProductCode] onto Products[Code] — the line appears.
  3. Double-click the line: read the cardinality (1:*) and the filter direction (single) — the healthy default configuration.
  4. Proof by the visual: create a bar chart Category × Amount. Delete the relationship to SEE the symptom (same totals everywhere), then recreate it.
Your first working multi-table model — and the symptom of the missing relationship recognised for life.

Step-by-step script — link like Zineb (5 min)

1) Import the Products table alongside Sales. 2) In the Model view, drag Sales[ProductCode] onto Products[Code]: the line appears. 3) Double-click the line and read the cardinality (1:*) and the direction (single). 4) Create a bar chart Category × Amount and verify that the two tables talk to each other. 5) Delete the relationship, observe the identical totals everywhere, then recreate it — diagnosis No. 1 is anchored.

If the relationship refuses the 1:*, look for duplicates or blanks in the key on the dimension side and clean them in Power Query.

Copy-paste template — my relationship check-list

Relationship: Sales[ProductCode] → Products[Code]  (1:*)
[ ] Unique key column on the dimension side (no duplicate, no blank)
[ ] Cardinality read: 1:*
[ ] Filter direction: single (1 → *)
[ ] Auto-detected relationship checked in the Model view
Diagnosis: identical totals everywhere → missing or broken relationship.

Cross category and amount

Zineb links Sales and Products: the visual Category × Amount works without a formula.

Replace a VLOOKUP

Zineb no longer writes a lookup: the relationship propagates the product name from the dimension.

Diagnose a frozen total

All totals identical per category? Zineb immediately checks the relationship in the Model view.

Clean a key

A key with duplicates blocks the 1:*: Zineb removes the duplicates on the dimension side in Power Query.

✓ Best practices

  • Link the tables by the key column (unique on the dimension side).
  • Keep the single filter direction (1 → *): the default reflex.
  • Check every auto-detected relationship in the Model view.

✗ Common pitfalls

  • Leaving a false auto-detected relationship without checking.
  • Switching to bidirectional "to make it work" (ambiguity, slowness).
  • A key on the dimension side with duplicates or blanks (blocks the 1:*).

Key takeaways

  • Relationships replace VLOOKUPs: the model propagates filters between tables.
  • Normal cardinality: one-to-many, from the reference table (1) to the facts (*).
  • Always check auto-detected relationships in the Model view.
  • Identical totals on every row of a visual = missing or broken relationship: diagnosis No. 1.

48h challenge — apply it tomorrow

Like Zineb, link two tables with a relationship: drag the key from the facts onto the key of the dimension in the Model view, read 1:* and the single direction, then create a visual crossing the two tables. Delete the relationship and note the symptom (identical totals everywhere) before recreating it.

Section exit checklist
  • I can create a relationship between two tables by their key column.
  • I read the cardinality (1:*) and the filter direction (single) of a relationship.
  • I recognise diagnosis No. 1: identical totals = missing relationship.
Self-assessment 1-5: can I create and verify a relationship without hesitating?Self-assessment 1-5: did I reproduce the symptom of the missing relationship to recognise it?

Frequently asked questions

When should I use a bidirectional relationship?

Almost never: it creates calculation ambiguities and slowness, and shows up as bugs that are hard to diagnose. Legitimate cases exist (certain filterings by a dimension shared between two fact tables), but they are handled first by a good star schema (2.2) or, occasionally, by the DAX function CROSSFILTER. The default reflex remains the single direction.

My relationship refuses the 1:* cardinality: why?

Because the key column on the dimension side contains duplicates or blanks, whereas a dimension has a single row per element. Fix it in Power Query: Remove duplicates on the key, filter out null values. A clean reference table (a single unique row per element) is the condition for a reliable model.

Check your understanding

What does "identical totals on every row" of a visual mean?

Additional resources (optional, to go further)

Level 2 examFresh, harder questions to test what you've learned