Module 2 · Power BI DAX
Filter context and row context
The two ways DAX decides which rows a formula sees, and context transition, the rule that explains most "why is every row the same number?" mysteries.
About 25 minutes
The problem
A colleague adds a calculated column to the customers table to show each customer's lifetime sales:
DAX
Customer Sales (wrong) =
SUMX ( orders, orders[quantity] * orders[unit_price] * ( 1 - orders[discount_pct] / 100 ) )Every one of the 90 customers shows the same number: ₦830,541,245, the whole company's revenue. The formula is the same as the Revenue measure, which works perfectly in visuals. So why does it fail here?
The answer is evaluation context: the set of rows a formula can see when it runs. It's the single most important idea in DAX. Once it clicks, the rest of the language makes sense.
The concept
DAX has two kinds of context.
Filter context is the set of filters active when a measure is calculated: the row and column headers of the visual, slicers, page and report filters. A measure is recalculated for every cell, each with its own filter context. In a matrix of revenue by customers[region], the "Lagos" cell is calculated with the filter region = "Lagos", which flows down to orders.
Row context means "the current row" while DAX loops through a table. It exists in two places:
- Calculated columns: the formula runs once per row of the table.
- Iterators such as
SUMX,AVERAGEXandFILTER: the expression runs once per row of the table you pass in.
The crucial rule: row context doesn't filter anything. It only lets you read the current row's values, such as orders[quantity]. That's why the colleague's column fails: on the row for Kayode Distributors there's a row context on customers, but no filter, so SUMX ( orders, … ) loops over every order.
Context transition
CALCULATE turns the current row context into a filter context: "filter the model to this row". And every measure reference is wrapped in an invisible CALCULATE. So this column works:
DAX
Customer Sales = [Revenue]On Kayode's row, [Revenue] becomes CALCULATE ( [Revenue] ), which filters customers to Kayode, and that filter flows to orders. That's context transition, and it's why referencing a measure inside a row context behaves so differently from writing the same formula out in full.
Formula in a customers calculated column | Result on each row |
|---|---|
SUMX ( orders, … ) | the grand total: no filter on orders |
[Revenue] | that customer's revenue: context transition |
CALCULATE ( SUMX ( orders, … ) ) | that customer's revenue |
SUMX ( RELATEDTABLE ( orders ), … ) | that customer's revenue: only their related rows |
Example
Two calculated columns on customers. The first uses context transition, and the second uses it to group customers into bands:
DAX
Customer Sales = [Revenue]
Customer Band =
SWITCH (
TRUE (),
customers[Customer Sales] >= 20000000, "A: ₦20m+",
customers[Customer Sales] >= 10000000, "B: ₦10m–20m",
customers[Customer Sales] > 0, "C: under ₦10m",
"No sales"
)SWITCH ( TRUE (), … ) checks each condition in order and returns the first one that's true. It's DAX's tidy alternative to nested IFs.
Because Customer Band is a column, you can put it on an axis or in a slicer. A measure can't do that: measures give numbers for a filter context; columns create the categories you filter by.
Walkthrough
- On
customers, add the wrong column from the problem and confirm every row shows ₦830,541,245. - Change it to
Customer Sales = [Revenue]. Each customer now has its own number. Delete the wrong column. - Add
Customer Bandfrom the example and formatCustomer Salesas currency. - Build a table visual:
customers[Customer Band],[Active Customers],[Revenue]. You've just used a column (the band) to slice measures. - Add a slicer on
Date[Year]and select 2026. Revenue changes, but the bands don't, because they were calculated at refresh from lifetime sales.
Practice
Practice
How many customers are in band B: ₦10m–20m or above, that is, with lifetime sales of at least ₦10,000,000?
Practice
Which customer has the highest Customer Sales? Type the name as it appears in the data.
Task
4 minWrite a calculated column for customers called Order Count that counts each customer's order lines. It must give each customer their own number, so think about context transition. Paste it here.
Your work is checked for
- Named Order Count
- Uses a measure reference, CALCULATE or RELATEDTABLE, so each row is filtered to its customer
- Doesn't use COUNTROWS(orders) on its own, which would count every order
More practice
Drill · optional
On products, add Product Sales = [Revenue]. What are the lifetime sales of Detergent 900g (12)? (A rounded figure is fine.)
Drill · optional
HR data: on employees, add Leave Days = SUM(leave[days]) with no CALCULATE, after relating employees to leave. What number appears on every row?
Check your understanding
Answer every question to check.