Why Your DAX Measure Ignores Filters: The Real Story Behind Row Context, Filter Context, and Context Transition

Understand why DAX measures ignore filters, the pitfalls of row vs filter context, and how context transition drives unexpected Power BI results.

MP

A DAX measure that returns the wrong total or a slicer that filters nothing is almost always a context problem — and the root cause is rarely what you expect. By the end of this article, you’ll be able to spot where row and filter context interact, where they fail to, and why context transition is the real lever (and pitfall) behind most DAX surprises.

Row Context and Filter Context: Not Two Sides of the Same Coin

Most developers think of row context and filter context as siblings — but their interaction is strictly one-way unless you force it. Row context lives inside iterators (like SUMX, FILTER, ADDCOLUMNS) and calculated columns, evaluating expressions for each row without automatically filtering the rest of the model. Filter context, on the other hand, is what reports, visuals, and CALCULATE create: which rows are “visible” for aggregation.

It’s a common mistake to assume row context “filters” like filter context does — it does not. If you write this measure:


-- Naive measure: trying to sum line values using row context
[Naive Total] =
SUMX (
    Fact_Sales,
    Fact_Sales[Unit Price] * Fact_Sales[Quantity]
)

This works, but only because SUMX iterates the whole table within the existing filter context. Where this bites is when you try to reference related tables inside the iterator. For example:


-- Fails subtly: "looks" like it should filter Dim_Product, but doesn't
[Wrong Product Total] =
SUMX (
    Fact_Sales,
    Dim_Product[Category] = "Bikes"
        * Fact_Sales[Unit Price] * Fact_Sales[Quantity]
)

This doesn’t filter Fact_Sales to just “Bikes” — it evaluates Dim_Product[Category] in row context for each sale, but unless you trigger context transition, filter context is never applied on Dim_Product. The result: every row returns TRUE or FALSE, but the aggregation ignores the intent to filter.

This is the root of the infamous “my calculated column works, but my measure doesn’t” complaint: calculated columns always have row context, never filter context; measures the reverse. The only way to bridge the two is explicit context transition.

Context Transition: The Hidden Gear in DAX Calculations

Context transition is how DAX converts row context into filter context — but it only happens inside CALCULATE, CALCULATETABLE, or when referencing a measure (which is implicitly wrapped in CALCULATE). Iterators by themselves (SUMX, FILTER, etc.) do not perform context transition. This distinction is the single biggest source of “why did my DAX ignore this filter?” confusion among practitioners who are otherwise competent modelers.

Consider this classic trap:


-- Intended to sum sales for each product category, but fails
[Category Sales Naive] =
SUMX (
    VALUES ( Dim_Product[Category] ),
    [Total Sales]
)

Suppose [Total Sales] is defined as:


[Total Sales] = SUM ( Fact_Sales[Amount] )

This works as expected, because referencing a measure triggers context transition for each value of Dim_Product[Category]. But flip that logic into a calculated column and you get a different result:


-- In a calculated column on Dim_Product
[Category Sales Column] =
SUM ( Fact_Sales[Amount] )

This sums all sales, ignoring the current product row — because there’s only row context, and SUM doesn’t see filter context on Fact_Sales. To replicate the measure logic in a calculated column, you’d have to use CALCULATE:


-- Still in a calculated column, but now triggers context transition
[Category Sales Fixed Column] =
CALCULATE (
    SUM ( Fact_Sales[Amount] )
)

Now, for each row of Dim_Product, DAX applies a filter to Fact_Sales where the product keys match, via context transition. The difference is invisible until you write DAX that crosses tables — and then the wrong context silently produces the wrong answer.

Take a model with:

  • Fact_Sales (millions of rows, with ProductKey and Amount)
  • Dim_Product (distinct products, with ProductKey and Category)
  • Single-direction relationship: Dim_Product[ProductKey] to Fact_Sales[ProductKey]

Suppose you want to measure, for each category, the total sales of all products in that category. A naive approach:


[Category Sales Naive] =
SUMX (
    VALUES ( Dim_Product[ProductKey] ),
    CALCULATE ( SUM ( Fact_Sales[Amount] ) )
)

This works — context transition inside CALCULATE turns each ProductKey row context into a filter on Fact_Sales. But what if you leave out CALCULATE?


-- Subtle bug: no context transition, so SUM ignores the iterator's row context
[Category Sales Broken] =
SUMX (
    VALUES ( Dim_Product[ProductKey] ),
    SUM ( Fact_Sales[Amount] )
)

This returns the same total for every product, because SUM doesn’t see a filter on Fact_Sales: the row context from the iterator never becomes a filter context. You need CALCULATE or a measure reference (which is implicitly wrapped in CALCULATE) to force context transition. In practice, this means:

  • Always use a measure (never a naked aggregation) inside a row iterator to ensure context transition applies.
  • Or, if you must use a naked aggregation, explicitly wrap it in CALCULATE.

This is not a stylistic preference — it changes the result completely.

Why Calculated Columns and Measures Yield Different Results (And When to Use Each)

The row-vs-filter context distinction is more than academic: calculated columns and measures exist in different universes. Calculated columns evaluate once per row at refresh, always in row context, never with dynamic filter context from visuals or slicers. Measures evaluate per cell, always in the current filter context, and can perform context transition to simulate row context as needed.

Where this goes wrong is when someone ports logic from a calculated column into a measure, or vice versa, without accounting for context. For example, suppose you create a calculated column to flag top-selling products:


-- In Dim_Product, intended to tag top 10 by sales
[Is Top 10 Seller] =
RANKX (
    ALL ( Dim_Product ),
    CALCULATE ( SUM ( Fact_Sales[Amount] ) )
) <= 10

This works — as of the model refresh — but never updates for new sales unless you refresh the model, and can’t respect slicers or user-applied filters. If you try to move this logic into a measure:


[Is Top 10 Seller Measure] =
RANKX (
    ALL ( Dim_Product ),
    [Total Sales]
) <= 10

Now, the measure dynamically respects slicers and filters, because filter context is alive. But if you reference this measure in a calculated column, you get the wrong result — because the calculated column has no filter context. This is why you should only use calculated columns for static attributes, and measures for anything that must respond to interaction.

Context Collisions: Filter Context Overwritten, Not Added

One of the subtler traps: when you use CALCULATE with a filter argument on a column already filtered by the current filter context, CALCULATE overwrites the filter, not adds to it. This means:


-- Suppose visual filters Dim_Product[Category] to "Bikes"
[Overwritten Filter] =
CALCULATE (
    [Total Sales],
    Dim_Product[Category] = "Accessories"
)

This measure ignores the visual filter (for “Bikes”) and returns sales for “Accessories” only — the filter argument in CALCULATE replaces any existing filter on Dim_Product[Category]. If you want to add to the existing filter, use KEEPFILTERS:


[Added Filter] =
CALCULATE (
    [Total Sales],
    KEEPFILTERS ( Dim_Product[Category] = "Accessories" )
)

Now, if the visual is filtered to “Bikes”, KEEPFILTERS yields a blank (no rows match both “Bikes” and “Accessories”), not the sales for “Accessories”. This is a deliberate but often-missed distinction — and a common cause of “my measure ignores my slicers” complaints.

Checklist: Diagnosing Context Problems in DAX

  • If a measure returns the same value for every row: check for missing context transition inside an iterator.
  • If a calculated column doesn’t respect slicers or visuals: it’s working as designed — use a measure instead.
  • If slicers seem to have no effect on a measure: look for overwritten filter context inside CALCULATE (use KEEPFILTERS if needed).
  • If referencing related tables in a calculated column: you only have row context, so use CALCULATE to force context transition if you want to filter related fact tables.

The habit that saves time: when debugging, always write out which context exists at each step, and never assume an iterator or a calculated column “filters” in the way a measure does. The more your DAX crosses table boundaries, the more these distinctions bite — and the more context transition is the tool to fix it.

MP
Max Power
Published June 9, 2026  ·  Updated June 9, 2026