Forum Discussion
EfratY
5 months agoNew Member
DAX Behavior
Hi everyone, I’m trying to understand a behavior in DAX and would appreciate your help. Here are my measures: Total Orders = DISTINCTCOUNT( 'Sales Data'[OrderNumber] ) Aver...
- 5 months ago
HI EfratY
Here are the main points explaining what's going on:
- Any measure reference in DAX is automatically wrapped in a hidden
CALCULATE.
In this case,[Average Retail Price]is treated asCALCULATE ( [Average Retail Price] ). - Calling the
CALCULATEfunction within a row context causes context transition, where the row context is transformed into an equivalent filter context before evaluating the first argument ofCALCULATE. - In the
High Ticket Ordersmeasure, theFILTERfunction is an iterator which iterates over'Product Lookup', evaluating the 2nd argument expressionProduct Lookup'[ProductPrice] > [Average Retail Price]within a row context corresponding to each row of'Product Lookup'. - Due to this context transition, the values of all columns of
'Product Lookup'in the "current row", including'Product Lookup'[ProductPrice], are added as filters when evaluating[Average Retail Price]. This means[Average Retail Price]will return the average of just the single price on the current row iterated byFILTER. In other words[Average Retail Price]will return the same value as'Product Lookup'[ProductPrice]for any given row of'Product Lookup'iterated byFILTER. - As a result,
'Product Lookup'[ProductPrice] > [Average Retail Price]is logically equivalent to'Product Lookup'[ProductPrice] > 'Product Lookup'[ProductPrice], which is always false (a value is never greater than itself). - If you replace
[Average Retail Price]with[Overall Average Price], context transition still happens, but[Overall Average Price]removes filters on'Product Lookup'viaALL ( 'Product Lookup' )so the filters due to context transition are ignored. The value returned by[Overall Average Price]would however ignore any existing filters on'Product Lookup'so would not change due to any existing filters on'Product Lookup'. - Suggested solution: I would generally recommend storing the value of
[Average Retail Price]in a variable before callingFILTER. This avoids the whole context transition complication and should return the intended value. I would also recommend changingFILTER ( ... )to a boolean condition withinKEEPFILTERS. See below:
High Ticket Orders improved v1 = VAR AveragePrice = [Average Retail Price] RETURN CALCULATE ( [Total Orders], FILTER ( 'Product Lookup', 'Product Lookup'[ProductPrice] > AveragePrice ) )High Ticket Orders improved v2 = VAR AveragePrice = [Average Retail Price] RETURN CALCULATE ( [Total Orders], KEEPFILTERS ( 'Product Lookup'[ProductPrice] > AveragePrice ) )Some good reading on this topic:
https://www.sqlbi.com/articles/understanding-context-transition-in-dax/
- Any measure reference in DAX is automatically wrapped in a hidden
OwenAuger
5 months agoSuper User
HI EfratY
Here are the main points explaining what's going on:
- Any measure reference in DAX is automatically wrapped in a hidden
CALCULATE.
In this case,[Average Retail Price]is treated asCALCULATE ( [Average Retail Price] ). - Calling the
CALCULATEfunction within a row context causes context transition, where the row context is transformed into an equivalent filter context before evaluating the first argument ofCALCULATE. - In the
High Ticket Ordersmeasure, theFILTERfunction is an iterator which iterates over'Product Lookup', evaluating the 2nd argument expressionProduct Lookup'[ProductPrice] > [Average Retail Price]within a row context corresponding to each row of'Product Lookup'. - Due to this context transition, the values of all columns of
'Product Lookup'in the "current row", including'Product Lookup'[ProductPrice], are added as filters when evaluating[Average Retail Price]. This means[Average Retail Price]will return the average of just the single price on the current row iterated byFILTER. In other words[Average Retail Price]will return the same value as'Product Lookup'[ProductPrice]for any given row of'Product Lookup'iterated byFILTER. - As a result,
'Product Lookup'[ProductPrice] > [Average Retail Price]is logically equivalent to'Product Lookup'[ProductPrice] > 'Product Lookup'[ProductPrice], which is always false (a value is never greater than itself). - If you replace
[Average Retail Price]with[Overall Average Price], context transition still happens, but[Overall Average Price]removes filters on'Product Lookup'viaALL ( 'Product Lookup' )so the filters due to context transition are ignored. The value returned by[Overall Average Price]would however ignore any existing filters on'Product Lookup'so would not change due to any existing filters on'Product Lookup'. - Suggested solution: I would generally recommend storing the value of
[Average Retail Price]in a variable before callingFILTER. This avoids the whole context transition complication and should return the intended value. I would also recommend changingFILTER ( ... )to a boolean condition withinKEEPFILTERS. See below:
High Ticket Orders improved v1 =
VAR AveragePrice = [Average Retail Price]
RETURN
CALCULATE (
[Total Orders],
FILTER (
'Product Lookup',
'Product Lookup'[ProductPrice] > AveragePrice
)
)High Ticket Orders improved v2 =
VAR AveragePrice = [Average Retail Price]
RETURN
CALCULATE (
[Total Orders],
KEEPFILTERS ( 'Product Lookup'[ProductPrice] > AveragePrice )
)
Some good reading on this topic:
https://www.sqlbi.com/articles/understanding-context-transition-in-dax/