Forum Discussion
Slow Measures
- Anonymous8 years ago
With this measure, you are calling, and calculating the [Scr%_ApptDesir] measure 7 times...it only needs to be calculated once.
try this:
Grd_ApptDesir = VAR Score = [Scr%_ApptDesir] RETURN SWITCH ( TRUE (), Score >= .9, "A", Score >= .8, "B", Score >= .7, "C", Score >= .6, "D", "F" )
With this measure, you are calling, and calculating the [Scr%_ApptDesir] measure 7 times...it only needs to be calculated once.
try this:
Grd_ApptDesir =
VAR Score = [Scr%_ApptDesir]
RETURN
SWITCH ( TRUE (),
Score >= .9, "A",
Score >= .8, "B",
Score >= .7, "C",
Score >= .6, "D",
"F"
)Anonymous Thank you! I had considered using switch statements, but I wasn't sure how to use them. Thank you for a good example!
I modified those measures with switch statements, and load times for those grade measures took over 60% less time! I'd call that a solution.
I had suspected that Power BI recalculates measures when you call them, but not multiple times in the same measure! That seems buggy...
Also, those Scr% measures are based off of several others, so I just attributed the slow lead times to those "nested" calls. I have a thread that got lost somewhere about "3rd Tier Measures".
Thanks for your help!
- Anonymous8 years agoNot applicable
BillyT_350, no worries!
I realize that it seems buggy, but consider this:
Everytime the measure is called in an expression, the filter and/or context may be different. That is why it needs to be evaluated separately each time.
Paste the other measures that are referenced in the final measure, we may be able to optimize those as well.
- BillyT_3508 years agoHelper V
Anonymous That's true, but PBI already calculates a given measure any time you change a slicer. So why isn't a given measure just stored as a variable on the back end? It only needs to change if the user changes it's filtering, and any number of other measures, columns, etc, should be able to call it at will.
I'm already at work looking to add in switch statements to speed things up in other places in this report and other reports that I've made. What other types of recursive statements are there? A "switch" is basically a "case". What about "for/foreach"s?
Here is a sample of one of the Scr%s. Some of what I do here is necesary because of the way the data is structured.
[m_ApptTimes] is simply a sum(x)/sum(y)
I used [m_ApptTimes] > AVERAGE... because I have to use a function in a CALCULATE, and the average of one item is simply itself. However, if for some weird reason there are multiple values, using average should lessen the damage, vs a sum, product, etc.
In short, the Scr% codes check to see if the a metric falls within a certain range. If it it outside the range, it will produce a 100% or 0%. Otherwise, the percentage is calculated based on the range.
Now, these still load quite quickly. The worse offender by far is still the letter grade measures above.
Scr%_ApptDesir = IF(CALCULATE([m_ApptTimes] < AVERAGE('CUS_RANGE'[MIN_VALUE]) , FILTER('CUS_RANGE' , AND('CUS_RANGE'[BUS_UNIT] = SELECTEDVALUE('MTHLY'[IBU], "") , AND('CUS_RANGE'[SUMMARY_TYPE] = "ACCT_NBR" , 'CUS_RANGE'[METRIC_NAME] = "APPT_DESIR" ) ) ) ) , 1 , IF(CALCULATE([m_ApptTimes] > AVERAGE('CUS_RANGE'[MAX_VALUE]) , FILTER('CUS_RANGE' , AND('CUS_RANGE'[BUS_UNIT] = SELECTEDVALUE('MTHLY'[IBU], "") , AND('CUS_RANGE'[SUMMARY_TYPE] = "ACCT_NBR" , 'CUS_RANGE'[METRIC_NAME] = "APPT_DESIR" ) ) ) ) , 0 , CALCULATE( (1 - ( ([m_ApptTimes] - AVERAGE('CUS_RANGE'[MIN_VALUE])) / (AVERAGE('CUS_RANGE'[MAX_VALUE]) - AVERAGE('CUS_RANGE'[MIN_VALUE])) )) , FILTER('CUS_RANGE' , AND('CUS_RANGE'[BUS_UNIT] = SELECTEDVALUE('MTHLY'[IBU], "") , AND('CUS_RANGE'[SUMMARY_TYPE] = "ACCT_NBR" , 'CUS_RANGE'[METRIC_NAME] = "APPT_DESIR" ) ) ) ) ) )- Anonymous8 years agoNot applicable
BillyT_350, this will optimize your measure a little bit:
Scr%_ApptDesir = VAR FilteredTable = FILTER ( 'CUS_RANGE', 'CUS_RANGE'[BUS_UNIT] = SELECTEDVALUE ( 'MTHLY'[IBU], "" ) && 'CUS_RANGE'[SUMMARY_TYPE] = "ACCT_NBR" && 'CUS_RANGE'[METRIC_NAME] = "APPT_DESIR" ) RETURN IF ( CALCULATE ( [m_ApptTimes] < AVERAGE ( 'CUS_RANGE'[MIN_VALUE] ), FilteredTable ), 1, IF ( CALCULATE ( [m_ApptTimes] > AVERAGE ( 'CUS_RANGE'[MAX_VALUE] ), FilteredTable ), 0, CALCULATE ( ( 1 - DIVIDE ( ( [m_ApptTimes] - AVERAGE ( 'CUS_RANGE'[MIN_VALUE] ) ), ( AVERAGE ( 'CUS_RANGE'[MAX_VALUE] ) - AVERAGE ( 'CUS_RANGE'[MIN_VALUE] ) ) ) ), FilteredTable ) ) )You're using the same filter arugment in 3 different instances, so there's no need to scan the table 3 times to make the same table. Just assign it to a variable and off you go.
I'm also a little curious about this expression. is it a calculated column, or a measure? If it's a measure, I would do this to further optimize:
Scr%_ApptDesir = VAR FilteredTable = FILTER ( 'CUS_RANGE', 'CUS_RANGE'[BUS_UNIT] = SELECTEDVALUE ( 'MTHLY'[IBU], "" ) && 'CUS_RANGE'[SUMMARY_TYPE] = "ACCT_NBR" && 'CUS_RANGE'[METRIC_NAME] = "APPT_DESIR" ) VAR AverageMin = AVERAGE ( 'CUS_RANGE'[MIN_VALUE] ) /*Or this CALCULATE( AVERAGE ( 'CUS_RANGE'[MIN_VALUE] ), FilteredTable ) */ VAR AverageMax = AVERAGE ( 'CUS_RANGE'[MAX_VALUE] ) /*Or this CALCULATE( AVERAGE ( 'CUS_RANGE'[MAX_VALUE] ), FilteredTable ) */ RETURN IF ( CALCULATE ( [m_ApptTimes] < AverageMin, FilteredTable ), 1, IF ( CALCULATE ( [m_ApptTimes] > AverageMax, FilteredTable ), 0, CALCULATE ( ( 1 - DIVIDE ( ( [m_ApptTimes] - AverageMin ), ( AverageMax - AverageMin ) ) ), FilteredTable ) ) )I can't tell if you want the TRUE/FALSE statement to be in the calculate expression, or if you want to compare the [m_ApptTimes] measure to the Average of the Min/Max column, and have both of them respect the filtered table variable.
Even still, the first expression will be at least a little faster.