Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Register now to learn Fabric in free live sessions led by the best Microsoft experts. From Apr 16 to May 9, in English and Spanish.

Reply
brs09j
Helper II
Helper II

Context Transition

I'm sorry for all of these questions. This concept is really stumping me. Please let me know if what I gathered was incorrect. Thanks.

 

1)

 

Column = SUM ( Orders[Unit Price] ) will give you the total sum of the Unit Price column in all the rows of the new calculated column

 

2)

 

Column = CALCULATE ( SUM ( Orders[Unit Price] ) ) will give you the the individual Unit Price for every order in the calculated column.

 

3)

 

One-to-Many relationship

 

On 'Product' table and going to Sales table

 

Column = CALCULATE ( SUM ( Sales[Line Margin] ) ) will give you the the individual Line Margin amount for every order in the calculated column.

 

-Why don't we need RELATEDTABLE here to get the Line Margin data on the Product Table? Does this have to do with the CALCULATE function? When CALCULATE is used do we not need to have RELATED/RELATEDTABLE due to it's filtering capabilities that touch all of the tables related to it? This scenario was a One-to-Many relationship, is there a different set of rules for a Many-to-One relationship?

 

4)

 

When you call a measure with DAX, CALCULATE is automatically encompassing the measure. However, I thought measures require aggregators most of the time. Thus, if we are using SUMX with a CALCULATE you would think the iteration (lets say multiplication here) would happen first and then the sum would be calculated to give you one value. From the rules above though, it seems that when CALCULATE was added to the formula (Scenario 1 to Scenario 2 above) the results are formed for each individual row. This part is really confusing me. I thought only calculated columns work on the row to row basis.

 

Let me know if something isn't clear or if I need to add more info. Thanks again.

1 ACCEPTED SOLUTION
mattbrice
Solution Sage
Solution Sage

There are 2 contexts in Dax.  A ROW context and a FILTER context.  Only the FILTER context is applied to the data model to change the foundset of rows against which the the aggregation measure is finally computed.  

 

In your example #1, you had as a calculated COLUMN (not a measure) , Column = SUM ( Orders[Unit Price] ). Since this is a calculated column, the value is computed for each row one at the time.  For row #1, there is a row context which is the values of every column in the table for row #1.  But the filter context is empty.  Since there is a row context but no filter context, DAX sum's up all the values for 'Unit Price' is the Orders table. (Because, again, only Filter context impacts the model foundset, row context's do not).  For Row #2, Dax engine again sees a row context which is current values for every column in row #2.  But Filter context is again empty, so again DAX sum's up all the values for the 'Unit Price' in the Orders table.  In the end, for this new calculated column every row in the table will have the same value.

 

In your example #2, you wrapped the calculated column in a CALCULATE statement.  One of the main features of CALCULATE is to trigger what is called a "context transition".  It removes the the values from the row context and puts them into the filter context.  So with Column = CALCULATE ( SUM ( Orders[Unit Price] ) ), all the values of the columns of the current row are transitioned into the Filter context.  The Filter context is then applied to the model, and the SUM of Orders[Unit Price] is then computed.  Note though this may or may not be the value of 'Unit Price' for the current row. It is more precise to say the foundset is all the rows in 'Orders' for which the values of column 1, column 2, ...column N match the respective values of the current row being computed.  ok?  

 

#3 Filters automatically flow from the one side to the many side of a relationship.  if you put a calculated column in Product table =  SUM ( Sales[Line Margin] ), the result would be to sum all the 'Line Margin' values in the entire Sales table because the Filter context is still empty.  Do this:  CALCULATE ( SUM ( Sales[Line Margin] ) ), and again row is transitioned to filter context, applied to model, and values summed in Sales table is the now filtered foundset.  You don't need a RELATEDTABLE (which is is just a synonym for CALCULATETABLE anyway) because of automatic filter propagation from one -> many.  There is no automatic from many -> one unless you turn it "on" in the model (make relationships bi-directional - which imo should only be done after careful consideration).  

 

#4 is explained in #2 above.  You need to understand how context transition works.  Row contexts are created in two ways.  For calculated columns, row context exists for each row the value is calculated.  In Measures, you can programatically create a row context using an iterator function like SUMX and the the other 'X' functions and others such as ADDCOLUMNS.   So for SUMX per your example, multiplication does happen first, then the summation of the values.  What Dax does first is evaluate which rows you are iterating over.  Which rows are aggregated is at the heart of what makes Dax work - manipulating the filter context under which the aggregation function is computed.

 

Hope this makes sense...

View solution in original post

6 REPLIES 6
vanessafvg
Super User
Super User

@brs09j the italian DAX duo are offering this as a free course on there site, i highly recommend it!

 

https://www.sqlbi.com/learn/introducing-dax-video-course/





If I took the time to answer your question and I came up with a solution, please mark my post as a solution and /or give kudos freely for the effort 🙂 Thank you!

Proud to be a Super User!




mattbrice
Solution Sage
Solution Sage

There are 2 contexts in Dax.  A ROW context and a FILTER context.  Only the FILTER context is applied to the data model to change the foundset of rows against which the the aggregation measure is finally computed.  

 

In your example #1, you had as a calculated COLUMN (not a measure) , Column = SUM ( Orders[Unit Price] ). Since this is a calculated column, the value is computed for each row one at the time.  For row #1, there is a row context which is the values of every column in the table for row #1.  But the filter context is empty.  Since there is a row context but no filter context, DAX sum's up all the values for 'Unit Price' is the Orders table. (Because, again, only Filter context impacts the model foundset, row context's do not).  For Row #2, Dax engine again sees a row context which is current values for every column in row #2.  But Filter context is again empty, so again DAX sum's up all the values for the 'Unit Price' in the Orders table.  In the end, for this new calculated column every row in the table will have the same value.

 

In your example #2, you wrapped the calculated column in a CALCULATE statement.  One of the main features of CALCULATE is to trigger what is called a "context transition".  It removes the the values from the row context and puts them into the filter context.  So with Column = CALCULATE ( SUM ( Orders[Unit Price] ) ), all the values of the columns of the current row are transitioned into the Filter context.  The Filter context is then applied to the model, and the SUM of Orders[Unit Price] is then computed.  Note though this may or may not be the value of 'Unit Price' for the current row. It is more precise to say the foundset is all the rows in 'Orders' for which the values of column 1, column 2, ...column N match the respective values of the current row being computed.  ok?  

 

#3 Filters automatically flow from the one side to the many side of a relationship.  if you put a calculated column in Product table =  SUM ( Sales[Line Margin] ), the result would be to sum all the 'Line Margin' values in the entire Sales table because the Filter context is still empty.  Do this:  CALCULATE ( SUM ( Sales[Line Margin] ) ), and again row is transitioned to filter context, applied to model, and values summed in Sales table is the now filtered foundset.  You don't need a RELATEDTABLE (which is is just a synonym for CALCULATETABLE anyway) because of automatic filter propagation from one -> many.  There is no automatic from many -> one unless you turn it "on" in the model (make relationships bi-directional - which imo should only be done after careful consideration).  

 

#4 is explained in #2 above.  You need to understand how context transition works.  Row contexts are created in two ways.  For calculated columns, row context exists for each row the value is calculated.  In Measures, you can programatically create a row context using an iterator function like SUMX and the the other 'X' functions and others such as ADDCOLUMNS.   So for SUMX per your example, multiplication does happen first, then the summation of the values.  What Dax does first is evaluate which rows you are iterating over.  Which rows are aggregated is at the heart of what makes Dax work - manipulating the filter context under which the aggregation function is computed.

 

Hope this makes sense...

"#3 Filters automatically flow from the one side to the many side of a relationship.  if you put a calculated column in Product table =  SUM ( Sales[Line Margin] ), the result would be to sum all the 'Line Margin' values in the entire Sales table because the Filter context is still empty.  Do this:  CALCULATE ( SUM ( Sales[Line Margin] ) ), and again row is transitioned to filter context, applied to model, and values summed in Sales table is the now filtered foundset.  You don't need a RELATEDTABLE (which is is just a synonym for CALCULATETABLE anyway) because of automatic filter propagation from one -> many.  There is no automatic from many -> one unless you turn it "on" in the model (make relationships bi-directional - which imo should only be done after careful consideration).  "

 

 

Filters automatically flow from the one side to the many side of a relationship. Is this always or is it just when I have a calculated column or use CALCULATE?

It is the filter context that automatically flows from one -> many.  CALCULATE's role is to add/change/romove items from the filter context either by context transition or via its filter arguments.  So to be doubly clear, CALCULATE in and of itself doesn't cause filters to flow from one -> many; that is built into the Dax engine.  CALCULATE just modifies what filters are in filter context.

 

I copied the below from a great although complex article by Jeffrey Wang @ Microsoft. 

Logic-behind-magic-of-dax-cross-table

 

Calculate function performs the following operations:

  1. Create a new filter context by cloning the existing one.
  2. Move current rows in the row context to the new filter context one by one and apply blocking semantics against all previous tables.
  3. Evaluate each setfilter argument in the old filter context and then add setfilter tables to the new filter context one by one and apply blocking semantics against all tables that exist in the new filter context before the first setfilter table is added.
  4. Evaluate the first argument in the newly constructed filter context.

Make sense?

@mattbrice

 

I think so, thank you!

 

@mattbrice See above. Thanks.

Helpful resources

Announcements
Microsoft Fabric Learn Together

Microsoft Fabric Learn Together

Covering the world! 9:00-10:30 AM Sydney, 4:00-5:30 PM CET (Paris/Berlin), 7:00-8:30 PM Mexico City

PBI_APRIL_CAROUSEL1

Power BI Monthly Update - April 2024

Check out the April 2024 Power BI update to learn about new features.

April Fabric Community Update

Fabric Community Update - April 2024

Find out what's new and trending in the Fabric Community.