Forum Discussion
No CALCULATE Challenge -- Round #2
I learned a lot from the brilliant folks who participated in my previous challenge, so I wanted to explore another situation where I find CALCULATE highly useful and see how others manage to do without it.
In this scenario, I want to define a benchmark ratio as the ratio of sales to sales in North American Stores, preserving all filtering other than geography and channel. Using CALCULATE, this is simple:
Sales Benchmark Ratio =
VAR _BenchmarkSales =
CALCULATE (
[Sales Amount],
ALL ( Geography ),
Geography[ContinentName] = "North America",
ALL ( Channel ),
Channel[ChannelName] = "Store"
)
VAR _Ratio = DIVIDE ( [Sales Amount], _BenchmarkSales )
RETURN
_Ratio
The challenge is to write a measure without CALCULATE that can reproduce this robustly.
Here's an example of what the values should be with some filters applied:
For full credit, the measure should work regardless of what dimensions are used for slicers and what dimensions are used for rows and columns of the matrix. It should still work even if new dimensions are added to the data model.
Participants from last time:
tamerj1 AntrikshSharma Greg_Deckler Anonymous
Other friends who might like to join:
AlexisOlson and All,
I was completely convinced that No CALCULATE solution wasn't possible for such kind of problems. My belief is that filter context manipulation is exclusively CALCULATE branded. Yesterday while handling a DAX issue, AlexisOlson No CALCULATE challenge popped up in my mind. With a probable solution, I decided to try the first thing in the morning and seems it covers all the requirements.
Sales Benchmark Ratio 2 = VAR _BenchmarkSales = SUMX ( ALLSELECTED ( Geography[RegionCountryName] ), SUMX ( CROSSJOIN ( TREATAS ( { "North America" }, Geography[ContinentName] ), TREATAS ( { "Store" }, Channel[ChannelName] ) ), [Sales Amount] ) ) VAR _Ratio = DIVIDE ( [Sales Amount], _BenchmarkSales ) RETURN _Ratio
30 Replies
- tamerj1
Community Champion
AlexisOlson and All,
I was completely convinced that No CALCULATE solution wasn't possible for such kind of problems. My belief is that filter context manipulation is exclusively CALCULATE branded. Yesterday while handling a DAX issue, AlexisOlson No CALCULATE challenge popped up in my mind. With a probable solution, I decided to try the first thing in the morning and seems it covers all the requirements.
Sales Benchmark Ratio 2 = VAR _BenchmarkSales = SUMX ( ALLSELECTED ( Geography[RegionCountryName] ), SUMX ( CROSSJOIN ( TREATAS ( { "North America" }, Geography[ContinentName] ), TREATAS ( { "Store" }, Channel[ChannelName] ) ), [Sales Amount] ) ) VAR _Ratio = DIVIDE ( [Sales Amount], _BenchmarkSales ) RETURN _Ratio- AlexisOlson
Super User
tamerj1 Nice work! This does appear to satisfy the requirements, so I'll accept it. The trick here is using the implicit CALCULATE context transition that comes from referencing another measure, [Sales Amount].
For an extra challenge, can anyone modify this so that it works as a standalone measure? Maybe using SUMMARIZE since that can also do a context transition.
- tamerj1
Community Champion
AlexisOlson
Yes sure, I thought about that already but it does not look so niceSales Benchmark Ratio 2 = VAR _BenchmarkSales = SUMX ( SUMMARIZE ( CROSSJOIN ( ALLSELECTED ( Geography[RegionCountryName] ), CROSSJOIN ( TREATAS ( { "North America" }, Geography[ContinentName] ), TREATAS ( { "Store" }, Channel[ChannelName] ) ) ), Geography[RegionCountryName], Geography[ContinentName], Channel[ChannelName], "@Amt", SUM ( Sales[SalesAmount] ) ), [@Amt] ) VAR _Ratio = DIVIDE ( [Sales Amount], _BenchmarkSales ) RETURN _Ratio
- Greg_Deckler
Community Champion
tamerj1 Brilliant! Nice one Tamer. Going to add that one to my pattern of No CALCULATE tricks.
- tamerj1
Community Champion
Shall we expect a new one of your brilliant videos? Tag me if you do 😉
- CNENFRNL
Community Champion
Interesting challenge 👋 Looking forward to some tricky solutions as I've already spent more than half hour but no luck. I really doubt there exists an elegant, error-proof alternative, especially when some new slicers will intervene.
Off the track comment on CALCULATE
I admit that the idea to kick the ass of CALCUALTION is buzzy at the first glance; but with further comprehension of DAX, I'd like to say the mechanism of context transition triggered by CALCULATE is a genius one! It differentiates DAX from other traditional "context insensitive" lauguages like Excel formulas, SQL etc.
- AlexisOlson
Super User
Yeah, this example demonstrates what I consider the most important use case for CALCULATE -- modifying filter context in a simple and precise way that works in almost any context without needing revision. One can write a measure without CALCULATE that works for a specific visual but if it can't reliably be reused in other contexts, we're back to the bad old days of SSRS where we often need separate queries for each visual.
Much of my day-to-day work is building and maintaining common data models that feed dozens of thin reports (I don't know in advance every possible context that each measure will be used) and this type of situation (where just one or two filters need to be removed or adjusted) comes up frequently with benchmarks and the only way I can think to do it without CALCULATE is remove all filters and specify the ones I want to put back, which requires (as far as I can tell) including within the measure definition any dimension table I ever want to filter or slice on, which is a problem if new dimensions are ever introduced (and probably not great for performance either).
- Greg_Deckler
Community Champion
AlexisOlson Technically meets the letter of the law with regard to the challenge. 🙂
Sales Benchmark Ratio NC Cheat = VAR _BenchmarkSalesTable = CALCULATETABLE( 'Sales', ALL ( Geography ), Geography[ContinentName] = "North America", Channel[ChannelName] = "Store" ) VAR _Ratio = DIVIDE ( [Sales Amount 1], SUMX(_BenchmarkSalesTable,[SalesAmount]) ) RETURN _Ratio- ValtteriN
Community Champion
Expanding on this logic. I think one solutions would be to use functions such as NATURALINNERJOIN to create a similar table and then use SUMX. This was my first instinct when looking at this (assuming CALCULATETABLE is also forbidden). Otherwise, your solution should be the best.
- Greg_Deckler
Community Champion
AlexisOlson This one works but is slow. Sharing it in case someone has a brilliant idea on how to optimize
Sales Benchmark Ratio NC2 = VAR __Products = DISTINCT('Product'[ProductKey]) VAR __Promotions = DISTINCT(Promotion[PromotionKey]) VAR __GeoKeys = DISTINCT(SELECTCOLUMNS(FILTER(ALL('Geography'), [ContinentName] = "North America"), "GeoKey",[GeographyKey])) VAR __Stores = DISTINCT(SELECTCOLUMNS(FILTER(ALL(Stores), [GeographyKey] IN __GeoKeys),"StoreKey",[StoreKey])) VAR __Channels = DISTINCT(SELECTCOLUMNS(FILTER(ALL('Channel'), [ChannelName] = "Store"),"ChannelKey",[Channel])) VAR __BenchmarkSalesTable = FILTER( FILTER( FILTER( FILTER( ALL('Sales'), [ProductKey] IN __Products ), [PromotionKey] IN __Promotions ), [StoreKey] IN __Stores ), [channelKey] IN __Channels ) VAR __BenchmarkSales = SUMX(__BenchmarkSalesTable, [SalesAmount]) VAR __Ratio = DIVIDE ( [Sales Amount 1], __BenchmarkSales ) RETURN __Ratio- tamerj1
Community Champion
"It should still work even if new dimensions are added to the data model." said AlexisOlson
- AlexisOlson
Super User
Greg_Deckler This is exactly the sort of approach I mentioned above and does not qualify as a full solution.
the only way I can think to do it without CALCULATE is remove all filters and specify the ones I want to put back, which requires (as far as I can tell) including within the measure definition any dimension table I ever want to filter or slice on, which is a problem if new dimensions are ever introduced (and probably not great for performance either).
- Greg_Deckler
Community Champion
AlexisOlson Understood but first step is to at least get something that returns the same values under the same conditions.
Also, not to get all nerdy but your calculation can't guarantee that adding additional dimensions will not break the calculation either. For example, if I add a GeographyType dimension and wire it to the Geography table and add the necessary rows for Stores, for example, a store tied to geography code 422 which is a State/Province and a Sales fact row for that Store then your calculation would always include that store row even when I specifically filter out State/Province in a slicer. So it's kind of a bogus requirement that the No CALCULATE version has to be better than the CALCULATE version.
- tamerj1
Community Champion
Hi AlexisOlson
Doesn't seem that simple 😅
it's 8:15pm right now here and I don't have access to my laptop. I hope not to close the subject until tomorrow same time. I will try to give it a shot anyway from my phone.
I just wanted to say that even this CALCULATE version of the measure won't work in all scenarios. For example if you have Country as a slicer or if you have any attribute from the Channel table other than Channel Name in the rows, I guess it won't work. So please be nice and grant a little bit of flexibility 😎- AlexisOlson
Super User
Adding a Country slicer doesn't seem to break it as far as I can tell but fair point about the Channel. I didn't use another ALL since there's only one visible column from that table, but I'll go ahead and edit that in.
- tamerj1
Community Champion
Not the best performance for sure
Sales Benchmark Ratio =
VAR _BenchmarkSales =
SUMX (
FILTER (
ALLSELECTED ( Sales ),
RELATED ( Geography[ContinentName] ) = "North America"
&& RELATED ( Channel[ChannelName] ) = "Store"
),
Sales[Quantity] * Sales[Price]
)
VAR _Ratio =
DIVIDE ( [Sales Amount], _BenchmarkSales )
RETURN
_Ratio- CNENFRNL
Community Champion
Inspiring way addressing this tricky chanllenge; but I'm afraid
FILTER( ALLSELECTED( Sales ), RELATED( Geography[ContinentName] ) = "North America" && RELATED( Channel[ChannelName] ) = "Store" )would produce an empty table because there exist an external slicers Channel[ChannelName] and Geography[ContinentName]; when Channel[ChannelName]<>"Store" or Geography[ContinentName]<>"North America" are selected, the filtered table is empty.
- AnonymousNot applicable
AlexisOlson - OK, interesting....fair or foul to tinker with the data model and collapse the geography snowflake to simplify?
- AlexisOlson
Super User
Collapsing the snowflake to a star schema is fine.
- AnonymousNot applicable
Cool, thanks. Would you mind throwing the data file into your original post?
- AntrikshSharma
Community Champion
AlexisOlson Great challenge, but because of the requirement I don't think it is possible to make the solution dynamic, so CALCULATE wins, Yay!
Otherwise with the current status of the report I would write this:
Sales Benchmark Ratio 2 = VAR SalesAmount = [Sales Amount] VAR _BenchmarkSales = FILTER ( ALL ( Sales ), RELATED ( Geography[ContinentName] ) = "North America" && RELATED ( Channel[ChannelName] ) = "Store" && RELATED ( 'Product'[ClassName] ) IN VALUES ( 'Product'[ClassName] ) && RELATED ( Promotion[DiscountPercent] ) IN VALUES ( Promotion[DiscountPercent] ) ) VAR Result = DIVIDE ( SalesAmount, SUMX ( _BenchmarkSales, Sales[SalesAmount] ) ) RETURN Result