Microsoft Power BI • DAX and Measures

RELATED/RELATEDTABLE — Use RELATED/RELATEDTABLE for relationship navigation (intro)

Navigate star-schema relationships in DAX to pull columns and tables across model boundaries.

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

SECTION 1

Historical Context & Motivation

Relational databases have long relied on JOIN operations to combine rows from two or more tables based on a related column. When Microsoft introduced Power Pivot in 2010 and later expanded it into Power BI's in-memory columnar engine, the underlying philosophy shifted away from explicit SQL joins toward a model-driven navigation paradigm. Instead of writing JOIN clauses, analysts define relationships once in the data model, and the DAX language provides functions—chiefly RELATED and RELATEDTABLE—that traverse those relationships at query time. This design echoes the Entity-Relationship model formalized by Peter Chen in 1976, but implements it inside a columnar, compressed analytics engine optimized for aggregation rather than transactional throughput.

1976 • ER Model Published

Peter Chen formalizes the Entity-Relationship model, establishing the conceptual foundation for expressing relationships between data entities that later influenced Power BI's data modeling approach.

2009 • Power Pivot Preview

Microsoft releases the first preview of Power Pivot as an Excel add-in, introducing the xVelocity (VertiPaq) in-memory columnar engine and DAX, which natively supports relationship navigation without explicit JOINs.

2010 • RELATED & RELATEDTABLE Ship

The initial DAX specification includes RELATED and RELATEDTABLE as core navigation functions, enabling many-to-one and one-to-many lookups across model relationships inside calculated columns and measures.

2015 • Power BI Desktop Launches

Microsoft ships Power BI Desktop, democratizing self-service BI. The relationship view in Power BI Desktop makes RELATED and RELATEDTABLE accessible to a broader audience through drag-and-drop relationship management.

2020+ • Composite & DirectQuery Models

Power BI extends relationship semantics to composite models and DirectQuery, ensuring RELATED and RELATEDTABLE function consistently across import and live-connection scenarios.

The central question these functions address is deceptively simple: how do you access a value that lives in a different table from the one your current row context belongs to? In SQL you would write a JOIN, but DAX operates within a filter-context and row-context evaluation model where relationships are metadata, not runtime clauses. Understanding RELATED and RELATEDTABLE is therefore foundational to writing correct and performant DAX.

SECTION 2

Core Principles & Definitions

Before diving into syntax, it is essential to understand the data model primitives that make RELATED and RELATEDTABLE possible. Power BI's analytical model organizes tables into a star schema (or snowflake variant) where fact tables store transactional or event-level data and dimension tables store descriptive attributes. Relationships between tables are defined by matching key columns, and each relationship has a cardinality (one-to-many, one-to-one, many-to-many) and a cross-filter direction that governs how filters propagate.

1

Row Context

A row context is the implicit 'current row' that DAX iterates over when evaluating a calculated column or an iterator function like SUMX. RELATED and RELATEDTABLE both require an active row context to know which foreign key to follow.
2

Many-to-One (RELATED)

RELATED follows a many-to-one relationship from the current table (many side) to the lookup table (one side) and returns a single scalar value. Think of it as a VLOOKUP that uses the model relationship as its match criterion.
3

One-to-Many (RELATEDTABLE)

RELATEDTABLE traverses in the opposite direction—from the one side to the many side—and returns an entire table (a set of rows). It is equivalent to applying a filter on the related table for the current row's key value.
4

Filter Propagation

In Power BI's default single-direction cross-filter, filters flow from the one side to the many side. RELATED leverages this natural flow, while RELATEDTABLE effectively reverses it by gathering rows from the many side for a given one-side row.
5

Relationship Chains

RELATED can traverse multiple hops across a chain of relationships (e.g., Sales → Product → Category) in a single call, as long as each hop is many-to-one. RELATEDTABLE, however, only crosses one hop.
✦

KEY TAKEAWAY

Think of RELATED as looking up a contact in your phone by name—you have a unique identifier and you get back one record. RELATEDTABLE is like searching all your text messages for a given contact—you provide the contact and get back a collection of messages. The direction you travel (many→one vs. one→many) determines which function to use.

SECTION 3

Visual Explanation — Star Schema Navigation

Star Schema — RELATED vs RELATEDTABLE DirectionSales (Fact)SaleIDProductID (FK)CustomerID (FK)AmountDateProduct (Dim)ProductID (PK)ProductNameCategoryID (FK)PriceCustomer (Dim)CustomerID (PK)NameRegionCategory (Dim)CategoryID (PK)CategoryNameRELATED()many → oneRELATED()many → oneRELATED() chainmany → one → oneRELATEDTABLE()one → manyRELATED (solid line)RELATEDTABLE (dashed)
The diagram shows a star schema with a central Sales fact table connected to Product, Customer, and Category dimension tables. Solid cyan arrows represent RELATED calls (many-to-one direction), while the dashed pink arrow shows a RELATEDTABLE call (one-to-many). Notice the chained RELATED path from Sales through Product to Category.

The visual makes the directional nature of these functions explicit. When your row context is in the Sales table (the many side), calling RELATED(Product[ProductName]) follows the foreign key ProductID to the unique row in the Product dimension and returns a single scalar. Conversely, if your row context is in the Product table (the one side), calling RELATEDTABLE(Sales) gathers all Sales rows whose ProductID matches the current product, returning a table. This distinction—scalar return versus table return—is the fundamental differentiator between the two functions and dictates where each can appear in a DAX expression.

SECTION 4

How It Works — Evaluation Mechanics

To understand the internal mechanics, consider the evaluation model that the VertiPaq engine uses. Every DAX expression is evaluated within two orthogonal contexts: the filter context (which rows are visible) and the row context (which single row is currently being iterated). Both RELATED and RELATEDTABLE operate exclusively within a row context—they need a 'current row' to know which key value to follow. This is why these functions are typically used inside calculated columns (which always have a row context) or inside iterator functions like SUMX, AVERAGEX, and FILTER.

RELATED — Scalar Lookup Semantics

RELATED SYNTAX

RELATED(<column>)RELATED( <column> )

Where <column> is a fully qualified column reference (e.g., Product[ProductName]) in a table on the one side of a relationship. Returns a single scalar value of the same data type as the referenced column.

Internally, the engine resolves RELATED by reading the foreign key column of the current row, performing a hash lookup against the primary key index of the target dimension table, and returning the requested column's value. Because the target is guaranteed to have at most one matching row (the relationship is many-to-one), the return type is always a scalar. If no matching row exists—e.g., a dangling foreign key—RELATED returns BLANK(). Importantly, RELATED can chain across multiple hops: RELATED(Category[CategoryName]) called from a Sales row context will automatically traverse Sales → Product → Category, as long as each intermediate relationship is many-to-one.

RELATEDTABLE — Table-Valued Lookup Semantics

RELATEDTABLE SYNTAX

RELATEDTABLE(<table>)RELATEDTABLE( <table> )

Where <table> is the name of a table on the many side of a relationship relative to the current row context. Returns a table filtered to only those rows whose foreign key matches the current row's primary key.

RELATEDTABLE is semantically equivalent to CALCULATETABLE(<table>) when a row context is active on the one side. Under the hood, the engine performs a context transition: it takes the primary key of the current row, converts it into a filter argument, and applies that filter to the target table. Because the target is on the many side, the result can contain zero, one, or many rows. This is why RELATEDTABLE must be wrapped in an aggregation function (COUNTROWS, SUMX, etc.) whenever a scalar result is needed.

⚠

Common Pitfall

Using RELATED in a measure without an iterator creates an error because there is no row context. Measures evaluate in filter context only. To use RELATED inside a measure, wrap it in an iterator: SUMX(Sales, Sales[Qty] * RELATED(Product[Price])). Alternatively, use RELATED freely in calculated columns, which always have an implicit row context.

SECTION 5

Detailed Breakdown — Contexts, Directions & Patterns

The choice between RELATED and RELATEDTABLE depends on two factors: which table your row context belongs to and the cardinality of the relationship you intend to traverse. The following diagram and table formalize this decision process.

Decision Flowchart: RELATED vs RELATEDTABLEIs there a row context?NOYESCannot use RELATEDor RELATEDTABLE directlyWhich side are you on?MANY sideONE sideUse RELATED()Returns a scalar valuefrom the one-side tableUse RELATEDTABLE()Returns a tablefrom the many-side tableExamples// Calculated column in Sales:RELATED( Product[ProductName] )// Calculated column in Product:COUNTROWS( RELATEDTABLE(Sales) )
This decision flowchart encodes the key question: does a row context exist, and if so, are you on the many side (use RELATED) or the one side (use RELATEDTABLE)? The bottom panel shows typical code patterns for each scenario.
AspectRELATEDRELATEDTABLE
DirectionMany → One (follows FK to PK)One → Many (gathers rows by PK)
Return typeScalar (single value)Table (set of rows)
Multi-hopYes — can chain across multiple M:1 relationshipsNo — single hop only
Typical useCalculated column on fact table; iterator in a measureCalculated column on dimension table; argument to COUNTROWS, SUMX
SQL analogyLEFT JOIN … ON FK = PK (returning one column)Correlated subquery returning a filtered rowset

Side-by-side comparison of RELATED and RELATEDTABLE

SECTION 6

Worked Example — Building a Revenue Measure

Suppose we have a Sales fact table with columns SaleID, ProductID, Quantity, and Discount, and a Product dimension table with ProductID, ProductName, and UnitPrice. We want to create a measure that computes total net revenue, and a calculated column on the Product table that counts the number of sales per product.

Part A — Net Revenue Measure Using RELATED

  1. 1

    Step 1 — Identify the Data Model

    Sales[ProductID] is the foreign key linking to Product[ProductID] (primary key). The relationship is many-to-one from Sales to Product. Because UnitPrice lives in the Product table, we need RELATED to bring it into a Sales row context.
  2. 2

    Step 2 — Write the Measure with SUMX

    We use SUMX to iterate over Sales rows, providing the row context that RELATED requires:
    Net Revenue = SUMX( Sales, Sales[Quantity] × RELATED(Product[UnitPrice]) × (1 − Sales[Discount]) )
  3. 3

    Step 3 — Trace the Evaluation

    For each row in Sales, SUMX establishes a row context. Within that context, RELATED(Product[UnitPrice]) reads the current row's ProductID, finds the matching Product row, and returns UnitPrice. The expression multiplies Quantity × UnitPrice × (1 − Discount) per row, and SUMX aggregates the results.
  4. 4

    Step 4 — Verify with Sample Data

    If a Sales row has Quantity = 10, ProductID = 42, and Discount = 0.1, and Product[UnitPrice] for ProductID 42 is $25, then the row contribution is 10 × 25 × (1 − 0.1) = 10 × 25 × 0.9 =
    $225.00

Part B — Sales Count Column Using RELATEDTABLE

  1. 1

    Step 1 — Identify the Direction

    We are adding a calculated column to the Product table (the one side). We want to count rows from Sales (the many side). This is a one-to-many traversal, so we use RELATEDTABLE.
  2. 2

    Step 2 — Write the Calculated Column

    Because RELATEDTABLE returns a table, we wrap it in COUNTROWS to get a scalar:
    SalesCount = COUNTROWS( RELATEDTABLE(Sales) )
  3. 3

    Step 3 — Trace the Evaluation

    For each Product row, the calculated column's implicit row context provides the current ProductID. RELATEDTABLE(Sales) filters the Sales table to only those rows whose ProductID matches, and COUNTROWS counts them. If product 42 appears in 150 Sales rows, the result is
    150

SECTION 7

Strengths, Limitations & Alternatives

RELATED and RELATEDTABLE are elegant for navigating established model relationships, but they are not the only tools available in DAX. Understanding when to use them—and when to reach for alternatives—is critical for writing maintainable, performant expressions.

ConsiderationStrengthsLimitations
ReadabilityIntuitive syntax; the relationship is implicit and the code is self-documenting.Multi-hop chains can be opaque if the model is unfamiliar to the reader.
PerformanceLeverages pre-built hash indexes on relationship columns; very fast for VertiPaq.RELATEDTABLE on very large fact tables inside a row-by-row iterator can be slow; CALCULATE may be more efficient.
FlexibilityWorks automatically with any active relationship; no key columns need to be specified.Cannot use inactive relationships without USERELATIONSHIP; cannot handle virtual relationships (no model relationship defined).
Many-to-ManyN/ANeither function works directly with many-to-many cardinality; bridge tables or TREATAS are needed.

Strengths and limitations of RELATED and RELATEDTABLE

✦

KEY TAKEAWAY

RELATED and RELATEDTABLE are your go-to functions when the data model already has the relationships defined and you want to traverse them declaratively—much like dereferencing a pointer in C or following a foreign-key constraint in a relational database. When you need to navigate a relationship that doesn't exist in the model, consider LOOKUPVALUE (for ad-hoc scalar lookups) or TREATAS (for virtual relationships).

SECTION 8

Connection to Advanced DAX Patterns

RELATED and RELATEDTABLE form the introductory layer of a broader DAX pattern family centered on context transition and expanded tables. As your models grow in complexity—with role-playing dimensions, many-to-many bridges, and calculation groups—you will encounter scenarios where these introductory functions must yield to more powerful constructs.

FeatureRELATED / RELATEDTABLEAdvanced Alternative
Inactive relationshipsCannot use inactive relationships natively.CALCULATE( ..., USERELATIONSHIP(OrderDate, Date[Date]) )
Virtual relationshipsRequires a physical model relationship.TREATAS( VALUES(Budget[ProductID]), Product[ProductID] )
Context transitionRELATEDTABLE performs implicit context transition.CALCULATE( SUM(Sales[Amount]) ) performs explicit context transition within iterators.
Many-to-manyNot supported.Bridge tables with bidirectional filtering or CROSSFILTER.

RELATED/RELATEDTABLE vs. advanced DAX constructs

In subsequent lessons, you will explore how CALCULATE subsumes much of what RELATEDTABLE does via context transition, and how USERELATIONSHIP extends navigation to inactive paths—particularly useful in role-playing dimension scenarios such as a Date table linked to both OrderDate and ShipDate. Mastering RELATED and RELATEDTABLE now gives you the conceptual vocabulary—row context, relationship direction, scalar vs. table return—that underpins these advanced techniques.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain why RELATED always returns a scalar value while RELATEDTABLE returns a table. How does the cardinality of the relationship determine the return type?
Show answer
RELATED follows a many-to-one (or one-to-one) relationship, meaning the current row's foreign key maps to at most one row in the target table; therefore, the function can only ever resolve to a single value. RELATEDTABLE moves in the opposite direction—from the one side to the many side—so the current row's primary key may match zero, one, or many rows in the target table. Since the number of matching rows is indeterminate at design time, the function must return a table object. This mirrors the fundamental distinction between a foreign-key lookup (guaranteed unique result) and a correlated subquery (set-valued result) in relational database theory.

Question 2: BASIC CALCULATION

Given a Sales table with columns Quantity and ProductID, and a Product table with columns ProductID and Weight, write a DAX measure that calculates total shipped weight across all sales.
Show answer
The measure should iterate over Sales rows to obtain a row context, use RELATED to fetch the product weight, and multiply by quantity: Total Shipped Weight = SUMX( Sales, Sales[Quantity] × RELATED( Product[Weight] ) ). SUMX provides the row context that RELATED requires, and the many-to-one relationship from Sales to Product ensures each Sales row resolves to exactly one weight value. The outer SUMX aggregation then totals these per-row products. This pattern is analogous to a SQL query: SELECT SUM(s.Quantity * p.Weight) FROM Sales s JOIN Product p ON s.ProductID = p.ProductID.

Question 3: INTERMEDIATE

You need a calculated column on the Customer dimension table that shows each customer's average order amount. The Sales table has an Amount column. Write the DAX expression and explain why you chose RELATEDTABLE instead of RELATED.
Show answer
The calculated column on the Customer table is: Avg Order Amount = AVERAGEX( RELATEDTABLE(Sales), Sales[Amount] ). We use RELATEDTABLE because the row context is on the Customer table (the one side), and we need to retrieve multiple Sales rows (the many side) for the current customer. RELATED would be incorrect here because it navigates from many to one, and there is no many-to-one relationship from Customer to Sales. RELATEDTABLE filters Sales to only the rows matching the current CustomerID, and AVERAGEX iterates over that filtered table to compute the mean of the Amount column. This is functionally equivalent to CALCULATETABLE(Sales) under context transition.

Question 4: APPLIED

A retail analytics team has a model with Sales (fact), Product (dim), and Category (dim, linked to Product via CategoryID). They want a measure that computes total revenue for products in the 'Electronics' category only. Write the measure using RELATED within an iterator and a FILTER.
Show answer
One approach is: Electronics Revenue = SUMX( FILTER( Sales, RELATED( Category[CategoryName] ) = "Electronics" ), Sales[Quantity] × RELATED( Product[UnitPrice] ) ). The FILTER function iterates over Sales (establishing a row context), and RELATED chains from Sales through Product to Category to check the CategoryName. This multi-hop traversal works because both legs (Sales→Product and Product→Category) are many-to-one. The filtered table is then passed to SUMX for aggregation. Note that in production, using CALCULATE with a slicer or filter argument on Category[CategoryName] is often more efficient, but this pattern demonstrates RELATED's chaining capability and is useful when the filter logic cannot be expressed as a simple predicate on a single column.

Question 5: CRITICAL THINKING

A colleague argues that RELATEDTABLE is redundant because you can always achieve the same result with CALCULATETABLE(<table>) inside a row context (relying on context transition). Critically evaluate this claim: under what conditions are they equivalent, and can you identify a scenario where the choice between them has practical implications?
Show answer
The colleague is largely correct for standard star-schema models: when a row context exists on the one side of a relationship, RELATEDTABLE(Sales) and CALCULATETABLE(Sales) produce identical results because both perform context transition—converting the current row's key into a filter on the related table. However, there are important nuances. First, CALCULATETABLE accepts additional filter arguments and can incorporate USERELATIONSHIP to activate inactive relationships, while RELATEDTABLE cannot. Second, in models with ambiguous or multiple relationship paths, RELATEDTABLE always uses the active relationship, whereas CALCULATETABLE can be directed to a specific path. Third, readability differs: RELATEDTABLE makes the navigation intent explicit, whereas CALCULATETABLE's context transition is implicit and may confuse developers unfamiliar with the mechanism. From a performance standpoint, the VertiPaq engine typically generates identical query plans for both, so the difference is primarily semantic and maintainability-oriented. In practice, choose RELATEDTABLE for simple one-hop lookups and reserve CALCULATETABLE for scenarios requiring filter manipulation.

SUMMARY

Lesson Summary

Summary

In this lesson you learned that RELATED and RELATEDTABLE are the primary DAX functions for navigating relationships in a Power BI data model. RELATED follows many-to-one relationships and returns a scalar value from the lookup (dimension) table. RELATEDTABLE traverses one-to-many relationships and returns a table of matching rows from the fact table. Both functions require a row context to operate, which is provided by calculated columns or iterator functions like SUMX and FILTER.

The key decision rule is straightforward: if your current row context is on the many side and you need a value from the one side, use RELATED; if you are on the one side and need rows from the many side, use RELATEDTABLE. RELATED supports multi-hop chaining across consecutive many-to-one relationships, while RELATEDTABLE is limited to a single hop. Looking ahead, CALCULATE and USERELATIONSHIP extend these navigation capabilities to inactive relationships and more complex filter manipulations.

Varsity Tutors • Microsoft Power BI • RELATED/RELATEDTABLE — Use RELATED/RELATEDTABLE for relationship navigation (intro)