Why the DAX engine treats a table row differently than a data slice
Most DAX confusion comes from one source: there are two completely different ways the engine can "know" where it is, and they are not interchangeable.
Row Context is a cursor. It points to a specific row in a table and says: this one. It exists when you're inside a calculated column (which runs once per row) or inside an iterator function like SUMX, AVERAGEX, or MAXX (which move through a table row by row). Inside Row Context, you can reference a column value directly - Sales[Quantity] gives you the quantity for the current row.
Filter Context is a filter. It's the set of constraints imposed on the entire model at the moment your expression evaluates. Every visual on your report applies Filter Context - a slicer for Year=2025 narrows the data the engine sees. A row on a matrix for Category="Electronics" narrows it further. Your measure runs inside whatever Filter Context exists at that point.
These two things feel similar because they both involve "which data are we looking at." They are not similar. Row Context doesn't filter the model - it doesn't change what SUM(Sales[Revenue]) returns. Filter Context does filter the model, but it doesn't give you access to individual column values row by row.
If you can't identify which context is active when you write an expression, you're guessing. And DAX will do exactly what you told it to do, not what you meant.
What is actually happening when you use an iterator?
An iterator is any function that ends in X: SUMX, AVERAGEX, MINX, MAXX, RANKX, and others. They all work the same way: take a table, walk through it row by row, evaluate an expression for each row, then aggregate the results.
The key thing: iterators create Row Context.
Total Revenue = SUMX(Sales, Sales[Quantity] * Sales[UnitPrice])
This walks through every row in the Sales table. For each row, it establishes Row Context — a cursor pointing at that row — and evaluates Sales[Quantity] * Sales[UnitPrice]. Then it sums all the results.
Inside the SUMX, you can reference Sales[Quantity] directly because Row Context makes that meaningful. You're always talking about this row's quantity.
Outside the SUMX — in a regular measure with no iterator — Sales[Quantity] on its own would be meaningless. There's no cursor. There's no "this row." The engine has no idea which quantity you're asking for.
Why doesn't SUM work in a calculated column?
It does work — but it probably doesn't do what you think.
In a calculated column, you're in Row Context. The column is being built row by row. When you write SUM(Sales[Revenue]) in a calculated column, you'd expect it to sum everything — and it does. Every row gets the same value: the grand total of all revenue.
That's almost never what you wanted. Usually you wanted the revenue for this row, which is just Sales[Revenue] — no aggregation needed, because Row Context already tells the engine which row you're on.
The confusion: SUM in a measure is fine because a measure runs inside Filter Context, and Filter Context determines which rows SUM aggregates across. SUM in a calculated column runs inside Row Context, and Row Context doesn't filter anything — so SUM sees the whole table.
Same function. Different context. Different result.
How does the engine handle nested iterators?
Each iterator creates its own Row Context for its own table. Nest two iterators, and you have two Row Contexts active simultaneously.
Something = SUMX( Orders, SUMX( RELATEDTABLE(OrderLines), OrderLines[Quantity] * OrderLines[UnitPrice] ) )
The outer SUMX walks through Orders, establishing Row Context for each order. The inner SUMX walks through the related order lines for that order, establishing its own Row Context for each line. Inside the inner iterator, OrderLines[Quantity] refers to the current order line row. Orders[OrderDate] refers to the current order row — that outer Row Context is still visible.
This works cleanly because each iterator knows which table it's iterating. What gets complicated is when you introduce CALCULATE inside an iterator — which transitions context — but that's Part 2.
The practical rule: nested iterators are fine. Just be explicit about which table each column reference belongs to. Sales[Column] versus Orders[Column] versus Products[Column] — always qualify your column references inside iterators so the engine (and you) know what's being referenced.
Why does a simple measure return the same number across all categories?
This one comes up constantly. You write a measure, drop it onto a matrix with Category on rows, and every row shows the same total. Usually it looks like the grand total repeated.
The reason is almost always one of two things:
1. The measure isn't responding to Filter Context.
A well-written measure like Total Revenue = SUM(Sales[Revenue]) will automatically respond to Filter Context. If the visual filters by Category, SUM only sees the rows that pass that filter. This works because SUM is filter-aware.
If every row shows the same number, the measure is either ignoring the Filter Context (often because ALL or ALLSELECTED is removing filters somewhere) or it's not connected to the data the filter applies to — which is usually a relationship problem.
2. The relationship isn't set up correctly.
If your Category column lives in a dimension table and the relationship to your Fact table is missing, broken, or pointed the wrong direction, filtering by Category in a visual won't filter the sales data. The measure runs against the full dataset regardless.
Before you blame the DAX, check the relationship. Model view, verify the join direction is dimension → fact. This fixes more "same number everywhere" bugs than any formula change.
How do these contexts compare in the real world?
A concrete side-by-side. Same model, same Sales table:
Calculated column — Row Context:
-- Column on the Sales table Sales[LineTotal] = Sales[Quantity] * Sales[UnitPrice]
Row Context is active. Sales[Quantity] gives you this row's quantity. This runs 5 million times if you have 5 million rows. Correct and efficient for row-level calculations.
Measure — Filter Context:
-- Measure Total Revenue = SUM(Sales[LineTotal])
No Row Context. Filter Context determines which rows SUM sees. Drop this on a card, it shows the filtered total. Drop it on a matrix by month, it shows monthly totals. The measure code doesn't change — the Filter Context does.
Measure with iterator — both:
-- Measure Avg Line Value = AVERAGEX(Sales, Sales[Quantity] * Sales[UnitPrice])
Filter Context is still active — the visual filters which rows end up in the Sales table passed to AVERAGEX. Then AVERAGEX creates Row Context to evaluate the expression for each of those filtered rows. Both contexts are present. This is the normal state for any non-trivial measure.
Frequently asked questions
Can a measure have Row Context?
Not by default. Measures run in Filter Context. But when a measure calls an iterator — SUMX, AVERAGEX, and so on — that iterator creates Row Context for the duration of its execution. So a measure can contain Row Context without being Row Context.
What happens if I use a filter inside an iterator?
CALCULATE inside an iterator triggers a context transition — it converts the current Row Context into an equivalent Filter Context. This is powerful and confusing in equal measure. It's the main subject of Part 2.
Why is my total row calculating differently than my row-level data?
Because the total row has different Filter Context. On a matrix, each row has a filter for its specific category (or date, or product). The total row has no such filter — it sees all the data. If your measure is well-written, this difference is intentional and correct. If it looks wrong, check whether your measure uses HASONEVALUE or ISINSCOPE to handle the total row explicitly.
Does Filter Context affect calculated columns?
No. Calculated columns are evaluated at data refresh time, not at report render time. There are no slicers, no visual filters — the report doesn't exist yet. Calculated columns run in Row Context only. This is why you can't reference a measure inside a calculated column: measures require Filter Context to mean anything, and calculated columns don't have it.
Stop guessing: the Evaluation Context Audit
When a DAX expression isn't doing what you expect, work through this before changing anything:
1. Where is this expression?
- Calculated column → Row Context is active, no Filter Context
- Measure → Filter Context is active, no Row Context (unless an iterator is involved)
- Inside an iterator → Row Context is active for that iterator's table; Filter Context from the visual is still present
2. What context does the function I'm using expect?
SUM,MIN,MAX,COUNT→ Filter Context (aggregators)SUMX,AVERAGEX,MAXX→ need a table to iterate; create Row ContextRELATED→ requires Row Context (follows a relationship from the current row)CALCULATE→ transitions context (more on this in Part 2)
3. Is the column reference I'm making valid in that context?
Table[Column]→ valid in Row Context (returns this row's value) or as part of an aggregation in Filter ContextTable[Column]alone in Filter Context with no aggregator → no good. Which row?
4. Is a relationship involved? Which direction does it run?
- Filtering flows from the "one" side to the "many" side by default
- If your dimension isn't filtering your fact, check the join direction
Run through these four questions before adding CALCULATE or wrapping things in iterators. Most DAX bugs are context bugs, and context bugs are diagnosable once you know where to look.