If you deal with Time Intelligence functions (TI) in DAX, you quickly reach the point where you can no longer use the standard TI functions. Very quickly you reach the ingenious site (daxpatterns.com) of Marco and Alberto from SQLBI, where they among other things show patterns for custom TI functions. The current article uses the TI pattern as an example to show how the filter context works, but this can generally be transferred to the functionality in DAX.
How the pattern looks like
Custom TI functions can be useful and necessary if the standard TI functions of DAX are no longer sufficient. The pattern from SQLBI looks as follows:
[SalesYTD] :=
CALCULATE (
[Sales],
FILTER (
ALL ( ‚Date‘ ),
‚Date'[Year] = MAX ( ‚Date'[Year] ) &&
‚Date'[Date] <= MAX ( ‚Date'[Date] )
)
)
https://www.daxpatterns.com/time-patterns/
What the pattern does
Let’s try to follow the pattern by using the (green highlighted) example value 3rd of January 2018:
The pattern does the following:
- The FILTER function is an iterator. It iterates over the complete and unfiltered Calendar table row by row, because ALL( ‚Calendar‘) is ignoring the existing filters of the filter context (the date 3rd of January 2018) on the Calendar table.
- Due to the existing row context created by the FILTER function, it iterates over each row in the Calendar table and compares two things:
- If the given ‚Year‘ (marked red) is equal to the maximum ‚Year‘ (marked green) respecting the current filter context (which is 3rd of January 2018) and
- if the given ‚Date‘ (marked red) is smaller than or equal to the maximum ‚Date‘ (marked green) of the current filter context (which again is 3rd of January 2018).
- At the end, when all filters are set, the measure [Sales] is evaluated.
Now please answer the following question: Why is MAX( 'Calendar'[Year] )
and MAX( 'Calendar'[Date] )
evaluated under the existing »filter context«? Take some time and see if you can find a satisfactory answer.
Why it works
I have been using this pattern for a very long time and simply accepted that it works without really asking myself why it works. There is this already very old but wonderful article by Jeffrey Wang (read my interview with him here) which describes how cross filtering works in DAX. He describes that there are only 3 ‚targets of filter context‘ in the DAX language:
- A table reference (like ‚Sales‘),
VALUES( Table[Column] )
andDISTINCT( Table[Column] )
.
Nothing else gets effected by the filter context. But how does that relate to the TI pattern of our Italian friends, as neither MAX( 'Calendar'[Year] )
nor MAX( 'Calendar'[Date] )
contain one of the 3 targets from above?
It’s all about syntax sugar
Since DAX was developed for business users, „the original design goal was to make syntax as simple as possible in common usage patterns while maintaining a coherent and semantically sound language model“ (quote Jeffrey Wang, from my interview with him). That’s why there is so much ’syntax sugar‘ in DAX, which means that a more complex DAX expression is hidden in a simpler one.
I think that most DAX users know that
=
CALCULATE ( [Total Sales], Product[Color] = „Red“ )
is internally converted to
=
CALCULATE (
[Total Sales],
FILTER ( ALL ( Product[Color] ), Product[Color] = „Red“ ).
)
just to make the DAX entry point for beginners easier. The same applies to all aggregate functions that accept a column reference such as Table[Column] as the only argument, such as SUM( Table[Column] )
and also MAX( Table[Column] )
. Jeffrey describes it in the above mentioned blog article as follows:
Note that DAX function Sum(T[C]) is just a shorthand for SumX(T, [C]), the same is true for other aggregation functions which take a single column reference as argument. Therefore the table in those aggregation functions is filtered by filter context.
Good bye syntax sugar
Armed with this knowledge, let’s have a look at the TI pattern without syntax sugar:
Since each MAX( Table[Column] )
expression is represented internally by MAXX( Table, Table[Column] )
, the circle closes here: The first parameter of MAXX() is a table reference and this belongs to the 3 targets of the filter context. All this can of course be transferred to all other DAX expressions.
And now to be honest, was that the answer you would have given me to my question?! 😉
Cheers from Germany,
Lars
Lars ist Berater, Entwickler und Trainer für Microsoft Power BI. Er ist zertifizierter Power BI-Experte und Microsoft Trainer. Für sein Engagement in der internationalen Community wurde Lars seit 2017 jährlich durch Microsoft der MVP-Award verliehen. Lies hier mehr…
Sumit Malik meint
Hi Lars,
Hope you are doing well.
Even, I am still not able to understand why/how MAX is working in current filter context
Could you please provide me some link, from where I can get DAX from very basics
As per my understanding FILTER ALL – Returns all table and after that on complete table below filter is applied
Date'[Year] = MAX ( ‚Date'[Year] ) &&
‚Date'[Date] <= MAX ( ‚Date'[Date]
So when this logic runs for filter context 03.01.2018, then from full table we will pick all date which are = MAX ( ‚Date'[Year] )
2) what is MAXX how it work, SUMX iterate row by row and SUM, how MAXX works?