Calculation groups
Introducing calculation groups
o New feature DAX since late 2019
o Reduce the number of measures, by automating
repetitive patterns
o Useful in multiple scenarios
o Require some deep understanding of DAX
Basic measures
Any model contains several basic measures, like the following four:
Sales Amount := SUMX ( Sales, Sales[Quantity] * Sales[Net Price] )
Total Cost := SUMX ( Sales, Sales[Quantity] * Sales[Unit Cost] )
Margin := [Sales Amount] - [Total Cost]
Sales Quantity := SUM ( Sales[Quantity] )
Aggregation measures
Most measures are useful also in a pre-aggregated way.
For example, YTD, MTD, SPLY are common aggregations required for most measures
YTD Sales Amount :=
CALCULATE (
[Sales Amount],
DATESYTD ( 'Date'[Date] )
)
YTD Total Cost :=
CALCULATE (
[Total Cost],
DATESYTD ( 'Date'[Date] )
)
YTD Margin :=
CALCULATE (
[Margin],
DATESYTD ( 'Date'[Date] )
)
YTD Sales Quantity :=
CALCULATE (
[Sales Quantity],
DATESYTD ( 'Date'[Date] )
)
Basic measures and their variations
o One measure for each variation/basic measure
o 10 basic measures, 6 variations means 60 new
measures
o Every new basic measure requires all the variations
o The model quickly becomes messy
• Finding consistent names is hard
• Finding the right measure is troublesome
• Any update requires significant effort
The SWITCH solution
Before calculation groups, SWITCH has been widely used to reduce the number of
measures
VAR Selection = SELECTEDVALUE ( 'Time Intelligence'[Aggregation] )
RETURN
SWITCH (
Selection,
"CY", [Sales Amount],
"YTD", [YTD Sales Amount],
"PY", [PY Sales Amount],
"QTD", [QTD Sales Amount]
)
Calculation items are “patterns”
You define a pattern, which is implemented for each measure automatically
YTD <Measure> :=
CALCULATE (
<Measure>,
DATESYTD ( 'Date'[Date] )
)
Calculation groups
Calculation groups combine several calculation items in a single group.
Consider a possible syntax like the following one (note: this syntax does not exists)
CALCULATION GROUP "Time Intelligence"
ITEM CY := <Measure>
ITEM PY := CALCULATE ( <Measure>, SAMPEPERIODLASTYEAR ( 'Date'[Date] ) )
ITEM QTD := CALCULATE ( <Measure>, DATESQTD ( 'Date'[Date] ) )
ITEM YTD := CALCULATE ( <Measure>, DATESYTD ( 'Date'[Date] ) )
Using calculation items
Creating calculation groups
o Currently, only Tabular Editor lets you define calc
groups
o Download from https://tabulareditor.github.io/
o Free, open source project written by Daniel Otykier
o Offers lots of features, including calc groups
SELECTEDMEASURE
o SELECTEDMEASURE is a special function
o Acts as a placeholder for the measure changed
o Available only in calculation items
Changing the format string
Calculation items lets you change the format string too, if the default one does not fit well.
Can be dynamically changed, using SELECTEDMEASUREFORMATSTRING
CALCULATION GROUP "Time Intelligence"
ITEM CY := SELECTEDMEASURE ()
ITEM Growth :=
DIVIDE (
SELECTEDMEASURE (),
CALCULATE (
SELECTEDMEASURE (),
SAMPEPERIODLASTYEAR ( 'Date'[Date] )
)
)
Format as a decimal number
Format as a percentage
Excluding specific measures
Some calculation items might not be applicable to specific measures.
ISSELECTEDMEASURE lets you make distinctions
IF (
ISSELECTEDMEASURE (
[Sales Amount], [Gross Amount],
[Discount Amount], [Sales Quantity],
[Total Cost], [Margin]
),
DIVIDE (
SELECTEDMEASURE (),
COUNTROWS ( 'Date' )
)
)
SELECTEDMEASURENAME
SELECTEDMEASURENAME returns the name of the selected measure, as a string.
IF (
NOT ( SELECTEDMEASURENAME () = "Margin %" ),
DIVIDE (
SELECTEDMEASURE (),
COUNTROWS ( 'Date' )
)
)
Using calculation items in DAX
Calculation items can be used in a DAX expression by using CALCULATE
CALCULATE (
[Sales Amount],
'Time Intelligence'[Time calc] = "YTD"
)
-- the previous code becomes
CALCULATE (
CALCULATE (
[Sales Amount],
DATESYTD ( 'Date'[Date] )
)
)
--
-- Calculation Item: YTD
--
CALCULATE (
SELECTEDMEASURE (),
DATESYTD ( 'Date'[Date] )
)
Calculation item application
A calculation item is first applied, then evaluated.
The two steps happen in different moments, it is important to understand it well
CALCULATE (
[Sales Amount],
'Time Intelligence'[Time calc] = "YTD"
)
-- the previous code becomes
CALCULATE (
CALCULATE (
[Sales Amount],
DATESYTD ( 'Date'[Date] )
)
)
--
-- Calculation Item: YTD
--
CALCULATE (
SELECTEDMEASURE (),
DATESYTD ( 'Date'[Date] )
)
Application happens on measure references
A measure reference is needed to perform calculation item application
CALCULATE (
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
),
'Time Intelligence'[Time calc] = "YTD"
)
-- the previous code becomes
CALCULATE (
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
)
--
-- Calculation Item: YTD
--
CALCULATE (
SELECTEDMEASURE (),
DATESYTD ( 'Date'[Date] )
)
Application happens on every measure
All the measures in the expression are subject to calculation item application
CALCULATE (
DIVIDE (
[Total Cost],
[Sales Amount]
),
'Time Intelligence'[Time calc] = "YTD"
)
-- the previous code becomes
CALCULATE (
DIVIDE (
CALCULATE (
[Total Cost],
DATESYTD ( 'Date'[Date] )
),
CALCULATE (
[Sales Amount],
DATESYTD ( 'Date'[Date] )
)
)
)
--
-- Calculation Item: YTD
--
CALCULATE (
SELECTEDMEASURE (),
DATESYTD ( 'Date'[Date] )
)
Application happens before evaluation
All the measures in the expression are subject to calculation item application
CALCULATE (
CALCULATE (
[Sales Amount],
'Date'[Calendar Year] = "CY 2009"
),
'Time Intelligence'[Time calc] = "YTD",
'Date'[Calendar Year] = "CY 2008"
)
-- the previous code becomes
CALCULATE (
CALCULATE (
CALCULATE (
[Sales Amount],
DATESYTD ( 'Date'[Date] )
),
'Date'[Calendar Year] = "CY 2009"
),
'Date'[Calendar Year] = "CY 2008"
)
--
-- Calculation Item: YTD
--
CALCULATE (
SELECTEDMEASURE (),
DATESYTD ( 'Date'[Date] )
)
1
2
3
The result is YTD of
2009, not 2008
Calculation items on complex expressions
Using calculation items on complex expressions (anything more complex than a single
measure reference) results in unexpected behaviors.
CALCULATE (
DIVIDE (
[Sales Amount],
SUMX (
VALUES ( 'Date'[Calendar Year month] ),
IF (
[Sales Amount] > 0,
[# Working Days]
)
)
),
'Time Intelligence'[Time calc] = "YTD",
'Date'[Calendar Year] = "CY 2008"
)
Application leads to a wrong evaluation
CALCULATE (
DIVIDE (
CALCULATE (
[Sales Amount],
DATESYTD ( 'Date'[Date] )
),
SUMX (
VALUES ( 'Date'[Calendar Year month] ),
IF (
CALCULATE (
[Sales Amount],
DATESYTD ( 'Date'[Date] )
) > 0,
CALCULATE (
[# Working Days],
DATESYTD ( 'Date'[Date] )
)
)
)
),
'Date'[Calendar Year] = "CY 2008"
)
CALCULATE (
DIVIDE (
[Sales Amount],
SUMX (
VALUES ( 'Date'[Calendar Year month] ),
IF (
[Sales Amount] > 0,
[# Working Days]
)
)
),
'Time Intelligence'[Time calc] = "YTD",
'Date'[Calendar Year] = "CY 2008"
)
Always use a single measure expression
Using a single measure as the expression to modify with a calculation item, leads to a much
better (and predictable) result
Sales WD :=
DIVIDE (
[Sales Amount],
SUMX (
VALUES ( 'Date'[Calendar Year month] ),
IF (
[Sales Amount] > 0,
[# Working Days]
)
)
)
CALCULATE (
[Sales WD],
'Time Intelligence'[Time calc] = "YTD",
'Date'[Calendar Year] = "CY 2008"
)
Implicit measures
o Calculation groups work with measures only
o Therefore, if a calculation group is created, implicit
measures are disabled
o Best practice: expose measures only
Measures can be used in reports
Columns cannot be aggregated
Multiple calculation groups in a report
Multiple calculation groups
A single model may contain multiple calculation groups
-------------------------------------
-- 'Time Intelligence'[Time calc]
-------------------------------------
--
-- Calculation Item: YTD
--
CALCULATE (
SELECTEDMEASURE (),
DATESYTD ( 'Date'[Date] )
)
-------------------------------------
-- 'Averages'[Averages]
-------------------------------------
--
-- Calculation Item: Daily AVG
--
DIVIDE (
SELECTEDMEASURE (),
COUNTROWS ( 'Date' )
)
Multiple calculation groups in DAX
In a single CALCULATE statement, one can apply multiple calculation groups.
The developer needs to define the order of application of the calculation groups.
CALCULATE (
[Sales Amount],
'Time Intelligence'[Time calc] = "YTD",
'Averages'[Averages] = "Daily AVG"
)
Daily AVG applied first
CALCULATE (
[Sales Amount],
'Time Intelligence'[Time calc] = "YTD",
'Averages'[Averages] = "Daily AVG"
)
CALCULATE (
DIVIDE (
[Sales Amount],
COUNTROWS ( 'Date' )
),
'Time Intelligence'[Time calc] = "YTD"
)
DIVIDE (
CALCULATE (
[Sales Amount],
DATESYTD ( 'Date'[Date] )
),
COUNTROWS ( 'Date' )
)
YTD applied first
CALCULATE (
[Sales Amount],
'Time Intelligence'[Time calc] = "YTD",
'Averages'[Averages] = "Daily AVG"
)
CALCULATE (
CALCULATE (
[Sales Amount],
DATESYTD ( 'Date'[Date] )
),
'Averages'[Averages] = "Daily AVG"
)
CALCULATE (
DIVIDE (
[Sales Amount],
COUNTROWS ( 'Date' )
),
DATESYTD ( 'Date'[Date] )
)
Multiple calculation groups
The precedence defines which of the formulas is generated by the application of a
calculation group. The higher the precedence, the earlier the application.
--
-- Daily Average applied first
--
DIVIDE (
CALCULATE (
[Sales Amount],
DATESYTD ( 'Date'[Date] )
),
COUNTROWS ( 'Date' )
)
--
-- YTD applied first
--
CALCULATE (
DIVIDE (
[Sales Amount],
COUNTROWS ( 'Date' )
),
DATESYTD ( 'Date'[Date] )
)
Calculation group precedence
o When using multiple calculation groups
o The application follows the precedence
o Higher precedence means earlier application
o Remember: application, not evaluation
o Evaluation happens after the application, on the
resulting formula
Reusing calculation items
In a calculation item you can use CALCULATE to invoke another calculation item, even if it
belongs to the same group
-------------------------------------------------------
-- Calculation Item: 'Time Intelligence'[YOY%]
-------------------------------------------------------
DIVIDE (
CALCULATE (
SELECTEDMEASURE (),
'Time Intelligence'[Time calc] = "YOY"
),
CALCULATE (
SELECTEDMEASURE (),
'Time Intelligence'[Time calc] = "PY"
)
)
Recursion in calculation items
By calling a calculation item from inside another calculation item, you might obtain a recursive
definition. In other words, an infinite loop. DAX does not support recursive definitions.
--
-- Calculation Item: Loop A
--
CALCULATE (
SELECTEDMEASURE (),
Infinite[Loop] = "Loop B"
)
--
-- Calculation Item: Loop B
--
CALCULATE (
SELECTEDMEASURE (),
Infinite[Loop] = "Loop A"
)
CALCULATE (
[Sales Amount],
Infinite[Loop] = "Loop A"
)
Recursion through measures
Recursion might appear in less-evident scenarios, like the following where the infinite loop
appears through a couple of measures.
--
-- Measures definition
--
MA := CALCULATE ( [Sales Amount], Infinite[Loop] = "A" )
MB := CALCULATE ( [Sales Amount], Infinite[Loop] = "B" )
-------------------------------------------------------
-- Calculation Group: Infinite[Loop]
-------------------------------------------------------
--
-- Calculation Item: A
--
[MB]
--
-- Calculation Item: B
--
[MA]
CALCULATE (
[Sales Amount],
Infinite[Loop] = "A"
)
Rule #1: Do not use complex expressions
CALCULATE (
DIVIDE (
[Sales Amount],
SUMX (
VALUES ( 'Date'[Calendar Year month] ),
IF (
[Sales Amount] > 0,
[# Working Days]
)
)
),
'Time Intelligence'[Time calc] = "YTD",
'Date'[Calendar Year] = "CY 2008"
)
Rule #2: Avoid recursion
-------------------------------------------------------
-- Calculation Item: 'Time Intelligence'[YOY%]
-------------------------------------------------------
DIVIDE (
CALCULATE (
SELECTEDMEASURE (),
'Time Intelligence'[Time calc] = "YOY"
),
CALCULATE (
SELECTEDMEASURE (),
'Time Intelligence'[Time calc] = "PY"
)
)
Calculation group best practices
o Only two rules:
• Do not use complex expressions when applying a
calculation item – only use a single measure.
• Do not use any kind of recursion, including the
simplest sideways recursion.

Calculation Groups - color 1 slide per page.pdf

  • 1.
  • 2.
    Introducing calculation groups oNew feature DAX since late 2019 o Reduce the number of measures, by automating repetitive patterns o Useful in multiple scenarios o Require some deep understanding of DAX
  • 3.
    Basic measures Any modelcontains several basic measures, like the following four: Sales Amount := SUMX ( Sales, Sales[Quantity] * Sales[Net Price] ) Total Cost := SUMX ( Sales, Sales[Quantity] * Sales[Unit Cost] ) Margin := [Sales Amount] - [Total Cost] Sales Quantity := SUM ( Sales[Quantity] )
  • 4.
    Aggregation measures Most measuresare useful also in a pre-aggregated way. For example, YTD, MTD, SPLY are common aggregations required for most measures YTD Sales Amount := CALCULATE ( [Sales Amount], DATESYTD ( 'Date'[Date] ) ) YTD Total Cost := CALCULATE ( [Total Cost], DATESYTD ( 'Date'[Date] ) ) YTD Margin := CALCULATE ( [Margin], DATESYTD ( 'Date'[Date] ) ) YTD Sales Quantity := CALCULATE ( [Sales Quantity], DATESYTD ( 'Date'[Date] ) )
  • 5.
    Basic measures andtheir variations o One measure for each variation/basic measure o 10 basic measures, 6 variations means 60 new measures o Every new basic measure requires all the variations o The model quickly becomes messy • Finding consistent names is hard • Finding the right measure is troublesome • Any update requires significant effort
  • 6.
    The SWITCH solution Beforecalculation groups, SWITCH has been widely used to reduce the number of measures VAR Selection = SELECTEDVALUE ( 'Time Intelligence'[Aggregation] ) RETURN SWITCH ( Selection, "CY", [Sales Amount], "YTD", [YTD Sales Amount], "PY", [PY Sales Amount], "QTD", [QTD Sales Amount] )
  • 7.
    Calculation items are“patterns” You define a pattern, which is implemented for each measure automatically YTD <Measure> := CALCULATE ( <Measure>, DATESYTD ( 'Date'[Date] ) )
  • 8.
    Calculation groups Calculation groupscombine several calculation items in a single group. Consider a possible syntax like the following one (note: this syntax does not exists) CALCULATION GROUP "Time Intelligence" ITEM CY := <Measure> ITEM PY := CALCULATE ( <Measure>, SAMPEPERIODLASTYEAR ( 'Date'[Date] ) ) ITEM QTD := CALCULATE ( <Measure>, DATESQTD ( 'Date'[Date] ) ) ITEM YTD := CALCULATE ( <Measure>, DATESYTD ( 'Date'[Date] ) )
  • 9.
  • 10.
    Creating calculation groups oCurrently, only Tabular Editor lets you define calc groups o Download from https://tabulareditor.github.io/ o Free, open source project written by Daniel Otykier o Offers lots of features, including calc groups
  • 11.
    SELECTEDMEASURE o SELECTEDMEASURE isa special function o Acts as a placeholder for the measure changed o Available only in calculation items
  • 12.
    Changing the formatstring Calculation items lets you change the format string too, if the default one does not fit well. Can be dynamically changed, using SELECTEDMEASUREFORMATSTRING CALCULATION GROUP "Time Intelligence" ITEM CY := SELECTEDMEASURE () ITEM Growth := DIVIDE ( SELECTEDMEASURE (), CALCULATE ( SELECTEDMEASURE (), SAMPEPERIODLASTYEAR ( 'Date'[Date] ) ) ) Format as a decimal number Format as a percentage
  • 13.
    Excluding specific measures Somecalculation items might not be applicable to specific measures. ISSELECTEDMEASURE lets you make distinctions IF ( ISSELECTEDMEASURE ( [Sales Amount], [Gross Amount], [Discount Amount], [Sales Quantity], [Total Cost], [Margin] ), DIVIDE ( SELECTEDMEASURE (), COUNTROWS ( 'Date' ) ) )
  • 14.
    SELECTEDMEASURENAME SELECTEDMEASURENAME returns thename of the selected measure, as a string. IF ( NOT ( SELECTEDMEASURENAME () = "Margin %" ), DIVIDE ( SELECTEDMEASURE (), COUNTROWS ( 'Date' ) ) )
  • 15.
    Using calculation itemsin DAX Calculation items can be used in a DAX expression by using CALCULATE CALCULATE ( [Sales Amount], 'Time Intelligence'[Time calc] = "YTD" ) -- the previous code becomes CALCULATE ( CALCULATE ( [Sales Amount], DATESYTD ( 'Date'[Date] ) ) ) -- -- Calculation Item: YTD -- CALCULATE ( SELECTEDMEASURE (), DATESYTD ( 'Date'[Date] ) )
  • 16.
    Calculation item application Acalculation item is first applied, then evaluated. The two steps happen in different moments, it is important to understand it well CALCULATE ( [Sales Amount], 'Time Intelligence'[Time calc] = "YTD" ) -- the previous code becomes CALCULATE ( CALCULATE ( [Sales Amount], DATESYTD ( 'Date'[Date] ) ) ) -- -- Calculation Item: YTD -- CALCULATE ( SELECTEDMEASURE (), DATESYTD ( 'Date'[Date] ) )
  • 17.
    Application happens onmeasure references A measure reference is needed to perform calculation item application CALCULATE ( SUMX ( Sales, Sales[Quantity] * Sales[Net Price] ), 'Time Intelligence'[Time calc] = "YTD" ) -- the previous code becomes CALCULATE ( SUMX ( Sales, Sales[Quantity] * Sales[Net Price] ) ) -- -- Calculation Item: YTD -- CALCULATE ( SELECTEDMEASURE (), DATESYTD ( 'Date'[Date] ) )
  • 18.
    Application happens onevery measure All the measures in the expression are subject to calculation item application CALCULATE ( DIVIDE ( [Total Cost], [Sales Amount] ), 'Time Intelligence'[Time calc] = "YTD" ) -- the previous code becomes CALCULATE ( DIVIDE ( CALCULATE ( [Total Cost], DATESYTD ( 'Date'[Date] ) ), CALCULATE ( [Sales Amount], DATESYTD ( 'Date'[Date] ) ) ) ) -- -- Calculation Item: YTD -- CALCULATE ( SELECTEDMEASURE (), DATESYTD ( 'Date'[Date] ) )
  • 19.
    Application happens beforeevaluation All the measures in the expression are subject to calculation item application CALCULATE ( CALCULATE ( [Sales Amount], 'Date'[Calendar Year] = "CY 2009" ), 'Time Intelligence'[Time calc] = "YTD", 'Date'[Calendar Year] = "CY 2008" ) -- the previous code becomes CALCULATE ( CALCULATE ( CALCULATE ( [Sales Amount], DATESYTD ( 'Date'[Date] ) ), 'Date'[Calendar Year] = "CY 2009" ), 'Date'[Calendar Year] = "CY 2008" ) -- -- Calculation Item: YTD -- CALCULATE ( SELECTEDMEASURE (), DATESYTD ( 'Date'[Date] ) ) 1 2 3 The result is YTD of 2009, not 2008
  • 20.
    Calculation items oncomplex expressions Using calculation items on complex expressions (anything more complex than a single measure reference) results in unexpected behaviors. CALCULATE ( DIVIDE ( [Sales Amount], SUMX ( VALUES ( 'Date'[Calendar Year month] ), IF ( [Sales Amount] > 0, [# Working Days] ) ) ), 'Time Intelligence'[Time calc] = "YTD", 'Date'[Calendar Year] = "CY 2008" )
  • 21.
    Application leads toa wrong evaluation CALCULATE ( DIVIDE ( CALCULATE ( [Sales Amount], DATESYTD ( 'Date'[Date] ) ), SUMX ( VALUES ( 'Date'[Calendar Year month] ), IF ( CALCULATE ( [Sales Amount], DATESYTD ( 'Date'[Date] ) ) > 0, CALCULATE ( [# Working Days], DATESYTD ( 'Date'[Date] ) ) ) ) ), 'Date'[Calendar Year] = "CY 2008" ) CALCULATE ( DIVIDE ( [Sales Amount], SUMX ( VALUES ( 'Date'[Calendar Year month] ), IF ( [Sales Amount] > 0, [# Working Days] ) ) ), 'Time Intelligence'[Time calc] = "YTD", 'Date'[Calendar Year] = "CY 2008" )
  • 22.
    Always use asingle measure expression Using a single measure as the expression to modify with a calculation item, leads to a much better (and predictable) result Sales WD := DIVIDE ( [Sales Amount], SUMX ( VALUES ( 'Date'[Calendar Year month] ), IF ( [Sales Amount] > 0, [# Working Days] ) ) ) CALCULATE ( [Sales WD], 'Time Intelligence'[Time calc] = "YTD", 'Date'[Calendar Year] = "CY 2008" )
  • 23.
    Implicit measures o Calculationgroups work with measures only o Therefore, if a calculation group is created, implicit measures are disabled o Best practice: expose measures only Measures can be used in reports Columns cannot be aggregated
  • 24.
  • 25.
    Multiple calculation groups Asingle model may contain multiple calculation groups ------------------------------------- -- 'Time Intelligence'[Time calc] ------------------------------------- -- -- Calculation Item: YTD -- CALCULATE ( SELECTEDMEASURE (), DATESYTD ( 'Date'[Date] ) ) ------------------------------------- -- 'Averages'[Averages] ------------------------------------- -- -- Calculation Item: Daily AVG -- DIVIDE ( SELECTEDMEASURE (), COUNTROWS ( 'Date' ) )
  • 26.
    Multiple calculation groupsin DAX In a single CALCULATE statement, one can apply multiple calculation groups. The developer needs to define the order of application of the calculation groups. CALCULATE ( [Sales Amount], 'Time Intelligence'[Time calc] = "YTD", 'Averages'[Averages] = "Daily AVG" )
  • 27.
    Daily AVG appliedfirst CALCULATE ( [Sales Amount], 'Time Intelligence'[Time calc] = "YTD", 'Averages'[Averages] = "Daily AVG" ) CALCULATE ( DIVIDE ( [Sales Amount], COUNTROWS ( 'Date' ) ), 'Time Intelligence'[Time calc] = "YTD" ) DIVIDE ( CALCULATE ( [Sales Amount], DATESYTD ( 'Date'[Date] ) ), COUNTROWS ( 'Date' ) )
  • 28.
    YTD applied first CALCULATE( [Sales Amount], 'Time Intelligence'[Time calc] = "YTD", 'Averages'[Averages] = "Daily AVG" ) CALCULATE ( CALCULATE ( [Sales Amount], DATESYTD ( 'Date'[Date] ) ), 'Averages'[Averages] = "Daily AVG" ) CALCULATE ( DIVIDE ( [Sales Amount], COUNTROWS ( 'Date' ) ), DATESYTD ( 'Date'[Date] ) )
  • 29.
    Multiple calculation groups Theprecedence defines which of the formulas is generated by the application of a calculation group. The higher the precedence, the earlier the application. -- -- Daily Average applied first -- DIVIDE ( CALCULATE ( [Sales Amount], DATESYTD ( 'Date'[Date] ) ), COUNTROWS ( 'Date' ) ) -- -- YTD applied first -- CALCULATE ( DIVIDE ( [Sales Amount], COUNTROWS ( 'Date' ) ), DATESYTD ( 'Date'[Date] ) )
  • 30.
    Calculation group precedence oWhen using multiple calculation groups o The application follows the precedence o Higher precedence means earlier application o Remember: application, not evaluation o Evaluation happens after the application, on the resulting formula
  • 31.
    Reusing calculation items Ina calculation item you can use CALCULATE to invoke another calculation item, even if it belongs to the same group ------------------------------------------------------- -- Calculation Item: 'Time Intelligence'[YOY%] ------------------------------------------------------- DIVIDE ( CALCULATE ( SELECTEDMEASURE (), 'Time Intelligence'[Time calc] = "YOY" ), CALCULATE ( SELECTEDMEASURE (), 'Time Intelligence'[Time calc] = "PY" ) )
  • 32.
    Recursion in calculationitems By calling a calculation item from inside another calculation item, you might obtain a recursive definition. In other words, an infinite loop. DAX does not support recursive definitions. -- -- Calculation Item: Loop A -- CALCULATE ( SELECTEDMEASURE (), Infinite[Loop] = "Loop B" ) -- -- Calculation Item: Loop B -- CALCULATE ( SELECTEDMEASURE (), Infinite[Loop] = "Loop A" ) CALCULATE ( [Sales Amount], Infinite[Loop] = "Loop A" )
  • 33.
    Recursion through measures Recursionmight appear in less-evident scenarios, like the following where the infinite loop appears through a couple of measures. -- -- Measures definition -- MA := CALCULATE ( [Sales Amount], Infinite[Loop] = "A" ) MB := CALCULATE ( [Sales Amount], Infinite[Loop] = "B" ) ------------------------------------------------------- -- Calculation Group: Infinite[Loop] ------------------------------------------------------- -- -- Calculation Item: A -- [MB] -- -- Calculation Item: B -- [MA] CALCULATE ( [Sales Amount], Infinite[Loop] = "A" )
  • 34.
    Rule #1: Donot use complex expressions CALCULATE ( DIVIDE ( [Sales Amount], SUMX ( VALUES ( 'Date'[Calendar Year month] ), IF ( [Sales Amount] > 0, [# Working Days] ) ) ), 'Time Intelligence'[Time calc] = "YTD", 'Date'[Calendar Year] = "CY 2008" )
  • 35.
    Rule #2: Avoidrecursion ------------------------------------------------------- -- Calculation Item: 'Time Intelligence'[YOY%] ------------------------------------------------------- DIVIDE ( CALCULATE ( SELECTEDMEASURE (), 'Time Intelligence'[Time calc] = "YOY" ), CALCULATE ( SELECTEDMEASURE (), 'Time Intelligence'[Time calc] = "PY" ) )
  • 36.
    Calculation group bestpractices o Only two rules: • Do not use complex expressions when applying a calculation item – only use a single measure. • Do not use any kind of recursion, including the simplest sideways recursion.