Userelationship with filter not displaying correctly

Hi all,

I have a simple table that has 2 columns with date, one called Create Date and the other called MQL Date. The Create Date has the active relationship to the Date table. See image #1.

I have two measures one with filter parameter, see image #2 and the other without filter parameter, see image #3.

The filter parameter filters for MQL Date after 1/1/2022. The outcome should be the same but it is not. Why am I getting two different values. See image #4

Can someone help me see what I am doing wrong? Thanks in advance.

image #1 - model
image

image #2 - with filter
image

image #3 - without filter
image

image #4 - outcome

WIP.pbix (17.3 MB)

1 Like

Hello @ysherriff,

Thank You for posting your query onto the Forum.

The answer is - “No, the outcome will not be the same”.

The reason being is, two different contexts are applied inside the “CALCULATE()” function i.e., “FILTER()” which is an iterator function and when there’s an iteration, “CALCULATE()” executes a context transition. And secondly, there’s a “USERELATIONSHIP()” function which is a context modifier.

So the way execution happens in your measure i.e., in “# of MQL with Filter” is in the following order -

CALCULATE starts from the current context

1). Then it starts to “evaluate” the filter arguments i.e., both row and filter context (In this case, “FILTER()” function since it’s an iterator and check the logic row-by-row).

2). Since it’s iterating row-by-row, “context transition” is bound to happen. That is, adds all the column filter to the new context (In this case, “‘Contacts Table’[Became an MQL Date]).

3). Now, it starts to evaluate the context modifiers (In this case, “USERELATIONSHIP()” function).

4). And then lastly, applies the filter arguments which are evaluated in “Step No. 1”.

In a simple way, here’s how the engine evaluates the measure - "# of MQL with Filter" at the back-end -

# of MQL with Filter = 
COUNTROWS(
    CALCULATETABLE(
        'Contacts Table' ,
        FILTER( 'Contacts Table' ,
            'Contacts Table'[Became an MQL Date] >= DATE( 2022, 1 , 1 ) ) ,
        USERELATIONSHIP( 'Contacts Table'[Became an MQL Date] , 'Date'[Date] ) ) )

So first, it creates a table i.e., “Contacts Table” then it starts the evaluation of the filter argument i.e., “FILTER()” function which iterates the logic row-by-row over the table where MQL Date >= 01/01/2022. Once the iteration over that column is done, it performs the context transition where that column “MQL Date” is added into a new context. Until now, the no. of rows generated is still 74.

Now, modifier starts working i.e., “USERELATIONSHIP()” function. And this is where, results starts to change. It creates the relationship with the “‘Date’[Date]” and converts the results of the “MQL Date” which is stored in a new context. So now, any rows prior to the condition i.e., 01/01/2022 is removed entirely from the “Contacts Table”. These are the rows which it removes from the table. Below is the screenshot provided for the reference -

And lastly, again applies the fiter arguments which were mentioned at the beginning i.e., in Step No. 1. (If you create a physical table with the DAX formula provided above by removing the “COUNTROWS()” function, you’ll see that the number of rows generated is 67 and not 74. And then if you check all the columns which contains the date information, you’ll observe that all the information will be available only from or after 01/01/2022 and not prior to that. That’s because, MQL Date was evaluated in a new context based on the logic and created a table where all information is only available after that date/logic. Below is the screenshot provided for the reference -)

Whereas in the measure - “# of MQLs without Filter”, “CALCULATE()” is only modifying the context i.e., “USERELATIONSHIP()” is simply changing the relationship from “Create Date” to “Become an MQL Date” by retaining all the rows of the table i.e., No Context Transistion Is Performed since there’s no iteration.

So at the back-end this is how engine evaluates the measure by introducing “ALL()” on the “Become an MQL Date” column -

# of MQLs without Filter = 
CALCULATE( COUNTROWS( 'Contacts Table' ) ,
    FILTER( ALL( 'Contacts Table'[Became an MQL Date] ) , 
                'Contacts Table'[Became an MQL Date] >= DATE( 2022 , 1 , 1 ) ) ,
    USERELATIONSHIP( 'Contacts Table'[Became an MQL Date] , 'Date'[Date] ) )

You can cross-verify it by creating a table with the below provided DAX formula in order to check the number of rows generated at the back-end. See this -

Table = 
CALCULATETABLE(
    'Contacts Table' , 
    FILTER( ALL( 'Contacts Table'[Became an MQL Date] ) , 
        'Contacts Table'[Became an MQL Date] >= DATE( 2022, 1 , 1 ) ) , 
    USERELATIONSHIP( 'Contacts Table'[Became an MQL Date] , 'Date'[Date] ) )

As you can observe in the above screenshot, in this case, it includes the information prior to the date - 01/01/2022 in the other date related columns.

Hoping you find this explanation useful and helps you in understanding how CALCULATE executes the orders and most importantly in which order.

Important Note: While cross-verifying the results, I’ve applied a filter on the column “Become an MQL Date” where the range is between “01/01/2022 - 31/01/2022” since that is the same range, you’ve selected in your slicer onto the report page So in case, if you want to cross-verify it, apply the same range and then check the results.

Thanks and Warm Regards,
Harsh

2 Likes

Excellent analysis Harsh. Excellent.

I need to get better grasp and understanding of filter context and context transition.

That is an area of learning that I need, especially how to utilize the “ALL” function.

Excellent post though Harsh. :smile:

Hello @ysherriff,

You’re Welcome! :slightly_smiling_face:

I’m glad that you find the explanation helpful in your learning curve.

Have a great weekend.

Thanks and Warm Regards,
Harsh

I do have one question though Harsh. Is it always best practice to put “All” within the filter context in calculate?

Hello @ysherriff,

Firstly, in this case, since you wanted to see the same results, you were not required to use the “FILTER()” function inside the filter argument of “CALCULATE()” since the expression which you wrote inside the “FILTER()” function is this -

'Contacts Table'[Became an MQL Date] >= DATE( 2022, 1 , 1 )

So the measure could’ve been simply written as -

# of MQLs without Filter =
CALCULATE( COUNTROWS( 'Contacts Table' ) ,
     'Contacts Table'[Became an MQL Date] >= DATE( 2022 , 1 , 1 ) ) ,
      USERELATIONSHIP( 'Contacts Table'[Became an MQL Date] , 'Date'[Date] ) )

Because in this case, at the back-end of the engine, it would’ve evaluated the measure in the same context as this one -

# of MQLs without Filter =
CALCULATE( COUNTROWS( 'Contacts Table' ) ,
        FILTER( ALL( 'Contacts Table'[Became an MQL Date] ) ,
                'Contacts Table'[Became an MQL Date] >= DATE( 2022 , 1 , 1 ) ) ,
        USERELATIONSHIP( 'Contacts Table'[Became an MQL Date] , 'Date'[Date] ) )

Now, coming to the usage of “ALL”. Since you wrote an expression like this -

# of MQL with Filter =
VAR _Become_an_MQL_Date =
FILTER( 'Contacts Table' ,
   'Contacts Table'[Became an MQL Date] >= DATE( 2022 , 01 , 01 ) )

RETURN
CALCULATE( COUNTROWS( 'Contacts Table' ) ,
    _Become_an_MQL_Date ,
    USERELATIONSHIP( 'Contacts Table'[Became an MQL Date] , 'Date'[Date] ) )

You explicitly removed the context from the column i.e., non-usage of “ALL()”. Because in this case, when “ALL()” is used, it’s used in the form of CALCULATE modifier and not simply/only as a table function. So by using “ALL()” in the form of this -

ALL( 'Contacts Table'[Became an MQL Date] )

It retains all the context of that particular column. So the formula would’ve been like this -

# of MQL with Filter =
VAR _Become_an_MQL_Date =
FILTER( ALL( 'Contacts Table'[Became an MQL Date] ) ,
    'Contacts Table'[Became an MQL Date] >= DATE( 2022 , 01 , 01 ) )

RETURN
CALCULATE( COUNTROWS( 'Contacts Table' ) ,
    _Become_an_MQL_Date ,
    USERELATIONSHIP( 'Contacts Table'[Became an MQL Date] , 'Date'[Date] ) )

Lastly, I won’t say it as a best practice that one needs to use “ALL” because if we eliminate the usage of “FILTER()” function inside the filter argument then by default there’ll always be a presence of “ALL()” at the back-end.

So for example, if you’ve slicers on your report, then we explicitly mention the use of “ALLSELECTED()” that’s because we want the figures to change in accordance with the selection made into the slicer. But if that’s not the case, then “ALL()” will be predicated by default.

The usage of these functions i.e., context modifiers depend upon the context that you’re trying to insert and the results that you’re trying to achieve.

Thanks and Warm Regards,
Harsh

2 Likes

Thanks again Harsh… I bookmarked this. Excellent explanation.