Something I've worked on a number of times and even spoke about at the ODTUG Kaleidoscope conference is MDX and specifically how to use it to do time related calcs like period-to-date functions and Time Balancing. I've seen quite a few posts on the subject and figured it was worth putting up some examples here.
More recent releases of Essbase will do time balancing natively, but period-to-date is something you still have to work out. My preferred method of doing this is to add an analytic dimension to the database and set the formula in one of the calculated members. For those of you who aren't familiar with the term, an analytic dimension is one that you add to your database that has a single default load member, all other members are calculated. In the case of time functionality I usually call this dimension [View] and have a default member called [Periodic]. I will then have some other members as needed, like [Time Balance], [QTD], [YTD], etc.
For Example:
The period-to-date functionality is pretty easy using the PeriodsToDate MDX function, the key is remembering to wrap it in a SUM function because the PTD function returns a member set, not a value.
YTD:
SUM(PeriodsToDate([Time].Generations(1), [Time].CurrentMember), [View].[Per])
QTD is almost identical with a modification to the generation reference:
SUM(PeriodsToDate([Time].Generations(2), [Time].CurrentMember), [View].[Per])
Time Balancing is little more involved. First you need to flag the members you want time balanced with a User Defined Attribute (UDA). Once you have that set you have to decide what kind of time balancing you wan to do. If you just want to grab the last child member, that's fairly easy.
CASE WHEN IsUDA([Measures].CurrentMember, "TB") THEN IIF(IsLeaf([Time].CurrentMember), [View].[Per],
(ClosingPeriod ([Time].Levels(0), [Time].CurrentMember), [View].[Per]))
ELSE [View].[Per] END
If you want to show the last value with data, known as Time Balance Skip missing, then you would use something like this:
CASE WHEN IsUDA([Measures].CurrentMember, "TB_Last") THEN
IIF(IsLeaf([Time].CurrentMember), [View].[Per],
IIF(NonEmptyCount ([Time].CurrentMember.Children, [View].[Per]) > 0,
([View].[Per], Tail(Filter(Leaves([Time].CurrentMember),
Not IsEmpty ([View].[Per]))).Item(0).Item(0)), MISSING))
ELSE [View].[Per] END
Now you'll want to modify your QTD and YTD functions to work with your Time balancing
QTD
CASE WHEN IsUDA([Measures].CurrentMember, "TB_Last") THEN
IIF(IsLeaf([Time].CurrentMember), [View].[Per],
IIF(NonEmptyCount ([Time].CurrentMember.Children, [View].[Per]) > 0,
([View].[Per], Tail(Filter(Leaves([Time].CurrentMember),
Not IsEmpty ([View].[Per]))).Item(0).Item(0)), MISSING))
ELSE SUM(PeriodsToDate([Time].Generations(2), [Time].CurrentMember), [View].[Per]) END
YTD
CASE WHEN IsUDA([Measures].CurrentMember, "TB_Last") THEN
IIF(IsLeaf([Time].CurrentMember), [View].[Per],
IIF(NonEmptyCount ([Time].CurrentMember.Children, [View].[Per]) > 0,
([View].[Per], Tail(Filter(Leaves([Time].CurrentMember),
Not IsEmpty ([View].[Per]))).Item(0).Item(0)), MISSING))
ELSE SUM(PeriodsToDate([Time].Generations(1), [Time].CurrentMember), [View].[Per]) END
More recent releases of Essbase will do time balancing natively, but period-to-date is something you still have to work out. My preferred method of doing this is to add an analytic dimension to the database and set the formula in one of the calculated members. For those of you who aren't familiar with the term, an analytic dimension is one that you add to your database that has a single default load member, all other members are calculated. In the case of time functionality I usually call this dimension [View] and have a default member called [Periodic]. I will then have some other members as needed, like [Time Balance], [QTD], [YTD], etc.
For Example:
The period-to-date functionality is pretty easy using the PeriodsToDate MDX function, the key is remembering to wrap it in a SUM function because the PTD function returns a member set, not a value.
YTD:
SUM(PeriodsToDate([Time].Generations(1), [Time].CurrentMember), [View].[Per])
QTD is almost identical with a modification to the generation reference:
SUM(PeriodsToDate([Time].Generations(2), [Time].CurrentMember), [View].[Per])
Time Balancing is little more involved. First you need to flag the members you want time balanced with a User Defined Attribute (UDA). Once you have that set you have to decide what kind of time balancing you wan to do. If you just want to grab the last child member, that's fairly easy.
CASE WHEN IsUDA([Measures].CurrentMember, "TB") THEN IIF(IsLeaf([Time].CurrentMember), [View].[Per],
(ClosingPeriod ([Time].Levels(0), [Time].CurrentMember), [View].[Per]))
ELSE [View].[Per] END
If you want to show the last value with data, known as Time Balance Skip missing, then you would use something like this:
CASE WHEN IsUDA([Measures].CurrentMember, "TB_Last") THEN
IIF(IsLeaf([Time].CurrentMember), [View].[Per],
IIF(NonEmptyCount ([Time].CurrentMember.Children, [View].[Per]) > 0,
([View].[Per], Tail(Filter(Leaves([Time].CurrentMember),
Not IsEmpty ([View].[Per]))).Item(0).Item(0)), MISSING))
ELSE [View].[Per] END
Now you'll want to modify your QTD and YTD functions to work with your Time balancing
QTD
CASE WHEN IsUDA([Measures].CurrentMember, "TB_Last") THEN
IIF(IsLeaf([Time].CurrentMember), [View].[Per],
IIF(NonEmptyCount ([Time].CurrentMember.Children, [View].[Per]) > 0,
([View].[Per], Tail(Filter(Leaves([Time].CurrentMember),
Not IsEmpty ([View].[Per]))).Item(0).Item(0)), MISSING))
ELSE SUM(PeriodsToDate([Time].Generations(2), [Time].CurrentMember), [View].[Per]) END
YTD
CASE WHEN IsUDA([Measures].CurrentMember, "TB_Last") THEN
IIF(IsLeaf([Time].CurrentMember), [View].[Per],
IIF(NonEmptyCount ([Time].CurrentMember.Children, [View].[Per]) > 0,
([View].[Per], Tail(Filter(Leaves([Time].CurrentMember),
Not IsEmpty ([View].[Per]))).Item(0).Item(0)), MISSING))
ELSE SUM(PeriodsToDate([Time].Generations(1), [Time].CurrentMember), [View].[Per]) END
Comments
Thank you
The Time Balance functionality like TB Last functionality is very interesting.
It would be really helpful for all if you could give once example by considering Sample Basic database.
Regards,
Suresh.
Regarding your YTD calc with Time Balancing, I guess that is for UDA with TB Last. How do I calculate for TB First? Awesome article.
Thank you.
did you ever do anything recreating the Time Balance/YTD functionality in an ASO cube where Years and Periods were combined?
we're struggling with the logic and I'd be interested if anyone has it.
I am using your YTD formula but it has slow performance , i was trying to use NONEMPYTUPLE, if you can spread some light how should i write NONEMPTYTUPLE in your mentioned formula will be greatly appreciated,
Thank you!
Don
I am using your YTD calculation and it works well on upper level members, however when I drill down on a customer dimention (100,000 + members)it gets hung up. I have used the NONEMPTYMEMBER and the retreive is much quicker however the YTD balances are not accurate for the dynamic hierarchies. For instance "NetSales" is comprised of "GrossSales" minus "Returns" - the YTD for "GrossSales" is calculating correctly, but since its skipping empty members and there are not returns everyday the "NetSales" for YTD is not factoring in the returns from prior days.
I changed the solve order to make YTD come after Account, however as I expected this did not change the result either.
Thanks.
In facet, I would like to calculate a Year to date in a member of a demension which is not Dynamic or Accounts in a ASO cube so is this possible ?
Thank you for your reply
It is not possible to have calculated members in stored hierarchies, so the only other way to calc is to leverage the outline.
So you can have alternate stored hierarchies in your time dim that define your YTD.
For example create an alternate hierarchy called JuneYTD with shared children members
Jan+
Feb+
Mar+
Apr+
May+
Jun+
Thank you for your reply yesterday.
I have only one little problem with one of your scripts:
I have a dimension Time Periods with this hierarchy :
Time Periods
--YearTotal(+)
----Total_Year(+)
------Jan(+)
------Feb(+)
------Mar(+)
------Apr(+)
------May(+)
------Jun(+)
------Jul(+)
------Aug(+)
------Sep(+)
------Oct(+)
------Nov(+)
------Dec(+)
----Quarters(+)
------Q1(+)
------Q2(+)
------Q3(+)
------Q4(+)
----Semesters(+)
------Sem1(+)
------Sem2(+)
And i would like to use your sum script to only calculate sum only from January to december so i wrote this : Sum(PeriodsToDate([Time Periods].Generations(4), [Dec]))
My problem is about generation tag Generations(4) because it will take i think all the elements in generation 4 of time periods including Q1 Q2 Q3 Q4 and Sem1 Sem2 so can you help me with this because i want only to have a sum of total year.
Thank you for your reply
ps: i thaught about using CHILDREN function 'http://download.oracle.com/docs/cd/E10530_01/doc/epm.931/html_esb_techref/techref.htm) but it doesen't work :(
I would love it if you could also share with me an example of how you can to QTD functionality in an ASO outline where Year and Time Periods are combined in one dimension. I really don't want to have to achieve this by manually creating shared members because that will be a maintenance nightmare. You can email your example to me at tmp1144@gmail.com
Thanks in advance!
Tamara
Maybe you can help me out. I need to be able in Financial Reports to select Yesterday as my POV and run the batches automatically. To achieve this I believe I need to create Yesterday dynamic Calculation member in Essbase . My YearTotal dimension looks like:
TotalYear
Q1+
Jan+(parent; below are level 0 members)
Jan1+
Jan 2+
Jan 3+
Jan 4+
How do I write a formula to get Yesterday?
If today is January 5, then I want my report would run for Jan 4.
I would appreciate any ideas or recommendations.
Thank you,
Ija
We have scenario were we are trying to implement transparent partition with BSO & ASO Cube.
As BSO supports DTS, so we have activated DTS & for ASO we used MDX scripts for DTS.
The requirement is to map BSO & ASO using transparent partition & we should see the impact of DTS.
Please suggest the best approach.
Great post, this helped us out tremendously. Do you happen to know what you do for Inception-to-Date?
Thanks again!
Mike
Thanks for all the useful information you post on this blog... you've reduced the rate at which I pull my hair out.
I would like to second the question above on Inception to Date...
- we have separate Year & Period dimensions;
Year dimension
- 2010
- 2011
- 2012
Period dimension
- YearTotal
- Q1
- Jan
- Feb
- Mar
- Q2
- Apr
- etc
I have also taken your advice and set up a View dimension, with 'Month' as the single stored member, and dynamic members for YTD, QTD, etc.
The ASO cube in question is designed to report on capital projects which can span more than one year....
I've set up an ITD (Inception to Date) View member, but cannot get the member formula to span across years.
Do you have any thoughts on how to approach this?
Cheers,
Alan
No need to respond to the above, I think I have a solution...
1) create one PeriodsToDate statement on the Year dimension for prior years
2)create a second one on the Period dimension for the periods in the current year.
3)add them together.
Not sure if it's the most efficient solution, but it works.
I have the code if anyone wants it, but be warned that this is my first day using MDX.....
Cheers,
Alan
It is easiest if you hard code the starting points. So if your database starts with Fiscal year 2010 and you run a typical Jan - Dec Time dim, your HTD formula would look like this
SUM(TupleRange(([FY2010], [Jan]), ([Year].CurrentMember, [Time].CurrentMember)), [View].[Per])
I will say that like anything else with MDX the size of your query is going to have a huge impact on your retrieval time. If HTD is a requirement for your database, I would argue that it would make the most sense to merge Periods and years into one dimension. Nonetheless, the above will work.
Regards,
Gary
I notice that using the sum function the performance is very bad if you compare agaist the MTD value where it is store the data. Instead using IFF and checking the month. I write it not as MDX language
If (Jan) then Jan
If (Feb) then (Feb+Jan)
.....
Do you notice a bad performance using sum when you start to use more members in your query and not the parent members?
I have been looking for a version of your YTD formulas for an ASO cube with Years and Periods combined with no luck. do you have an example you could send to me?
Tahnks,
Kenneth
kmsmisc@gmail.com
did you ever do anything recreating the Time Balance/YTD/QTD functionality in an ASO cube where Years and Periods were combined?
We have a the Time dim as
FY2012
->FY2012-Q1
->FY2012-Q1-Jan
->FY2012-Jan-W1
->FY2012-Q1-Day1
->2012-01-01
we're struggling with the logic and I'd be interested if anyone has it.
did you ever do anything recreating the Time Balance/YTD functionality in an ASO cube where Years and Periods were combined?
We have the Time dimension as
FY2012
->FY2012-Q1
->FY2012-Jan
->FY2012-Jan-W1
->FY2012-Q1-Day1
->2012-01-01
we're struggling with the logic and I'd be interested if anyone has it.
I am trying to make the TB_Last calculation to work but I am hitting a snag. Your code uses a tuple:
(
[View].[Periodic],
Tail(
Filter(
Leaves([Time].CurrentMember),
Not IsEmpty([View].[Periodic])
)
).Item(0).Item(0)
)
This is not working on our environment. when we try to use this part of the code, the whole formula breaks (does not return anything.)
If you could reply to my email: Marco.Rossodivita@perficient.com and I'll give you more details of the issue.
thanks in advance.
Marco A. Rossodivita
Iam working on one of member formula to achieve calculation entire USD.K data in cube multiply with some rate that store in ( By company ZCTT, PNFLIP and other No*** members )
Also iam trying to avoid calculation for Headcount ( PH00001 ) and also for Non currency accounts.
When i try to pass one month its working result as of expected. But when i pass range of periods for Eg ( FEB2016:MAY2016 ) then my calculation at parent level is not working as of expected / Formula error out.
Do you advice how i can able to achieve my result
IIF
( NOT
(
contains(Account.CurrentMember,(Descendants ([PH0001])))
OR
IsUda(Account.CurrentMember, "NonCurr")
),
Sum(
Descendants([ZCTT].CurrentMember,[ZCTT].Levels(0)),
([USD.K] *([APR2016],[PNFLIP],[No Organization],[No Product],[No Currency],[No Source],[No Scenario],[Working],([ZCTT].CurrentMember)
)))
,[USD.K].CurrentMember
)
Great post
we have Years and Periods combined in one dimension,we have the exact formula in your post for YTD but its not working, just pulling #missing for YTD for the Quarters i.e Q1-FY19 and also for FY19 period members
Please advice