Art of BI: Essbase ASO Version of BSO Dynamic Time Series

By | In Art of BI | November 19th, 2009

ASO does not provide an out-of-the-box version of dynamic-time-series (DTS) like its partner in crime, BSO. However, one straightforward approach to achieving this functionality is to implement a time aggregation dimension and leverage MDX functionality.  In this example we will house the Time Periods/Months (i.e.:  Mar, Apr, May, etc.) and the Years dimensions into two separate dimensions.  We then add an additional dimension called “Time Series” to the outline to support our ASO version of DTS.

[simage=89,400,y,center]

The Time Series dimension will consist of three sibling members: MTD, QTD, and YTD.  The data being loaded into our cube is always at the monthly/period level of granularity so for the MTD member no formula is required.  In the load rule file for the data load this column can load the simple value of ‘MTD’ since all data is loaded in at that level.

By adding in the MDX formulas below for the other two members, QTD and YTD, the final Time Series dimension should look like this:
[simage=90,400,y,center]

MDX Forumulas

/*QTD*/
CASE WHEN IsLevel([Time Periods].CurrentMember, 0)
THEN
/* Add all values from the first sibling of the member to itself */
sum(PeriodsToDate ([Time Periods].levels(2),[time periods].currentmember), [scenarios].currentmember )
ELSE
/* Meaningless to ask for QTD for any other [Year].dimension member. */
Missing
END

/*YTD*/
CASE WHEN IsLevel([Time Periods].CurrentMember, 0)
THEN
/* Add all values from the first sibling of the member to itself from the first fiscal period */
Sum( [Mar]:[Time Periods].CurrentMember, [Scenarios].CurrentMember)
ELSE
/* Meaningless to ask for QTD foranyother [Year].dimension member. */
Missing
END

Conclusion

By leveraging an additional “Time Series” dimension we free up our cube to be completely sliceable and not restricted to a particular scenario or multipled time dimension members in our time period dimension like some old-hat solutions.

Contact Us
Christian Screen
Christian is an innovator in analytics and data warehousing design, best practices, and delivery. With more than fifteenyears of decision support and data warehousing with key experiences at Office Depot HQ, Sierra-Cedar, and Capgemini, he oversees the Oracle Analytics Practice which includes the technical development and delivery of Oracle BI collaboration software, data warehouse solutions, Oracle BI/EPM projects, and packaged analytics solutions at Datavail.

Leave a Reply

Your email address will not be published.
Required fields are marked (*).

2 thoughts on “Art of BI: Essbase ASO Version of BSO Dynamic Time Series”
  1. Более правильный вариант – учесть полуаддитивные меры (например остатки).

    — Сделать измерение Time Series Dynamic (Label Only) (! не Multiple Hierarchies)
    — Пронести в измерении Time Series показатель [Per] (~) (Alias:Periodic)
    — Проставить для полуаддитивных показателей тэг Time Balance ("TB_Last")
    — Правильно осуществлять "продление" полуаддитивных показателей.

    [QTD] =
    CASE WHEN IsUDA([Measures].CurrentMember, "TB_Last") THEN
    IIF( IsLeaf([Year].CurrentMember) AND Not IsEmpty([Year].CurrentMember), [Time Series].[Per],
    IIF(IsLeaf([Year].CurrentMember),
    IIF (NonEmptyCount (MemberRange(Head( [Year].levels(0).members ).item(0).item(0),
    [Year].CurrentMember, LEVEL), [Time Series].[Per])> 0,
    ([Time Series].[Per], Tail (Filter (MemberRange(Head( [Year].levels(0).members ).item(0).item(0),
    [Year].CurrentMember, LEVEL),
    Not IsEmpty ([Time Series].[Per]))).Item(0).Item(0)), MISSING),
    IIF (NonEmptyCount (DESCENDANTS([Year].CurrentMember,10,LEAVES), [Time Series].[Per]) > 0,
    ([Time Series].[Per], Tail (Filter (DESCENDANTS([Year].CurrentMember,10,LEAVES),
    Not IsEmpty ([Time Series].[Per]))).Item(0).Item(0)),
    IIF (NonEmptyCount ([Year].levels(0).members, [Time Series].[Per]) > 0,
    ([Time Series].[Per], Tail (Filter ([Year].levels(0).members,
    Not IsEmpty ([Time Series].[Per]))).Item(0).Item(0)),MISSING))))
    ELSE SUM(PeriodsToDate([Year].Generations(2), [Year].CurrentMember), [Time Series].[Per] ) END

    [YTD] =
    CASE WHEN IsUDA([Measures].CurrentMember, "TB_Last") THEN
    IIF( IsLeaf([Year].CurrentMember) AND Not IsEmpty([Year].CurrentMember), [Time Series].[Per],
    IIF(IsLeaf([Year].CurrentMember),
    IIF (NonEmptyCount (MemberRange(Head( [Year].levels(0).members ).item(0).item(0),
    [Year].CurrentMember, LEVEL), [Time Series].[Per])> 0,
    ([Time Series].[Per], Tail (Filter (MemberRange(Head( [Year].levels(0).members ).item(0).item(0),
    [Year].CurrentMember, LEVEL),
    Not IsEmpty ([Time Series].[Per]))).Item(0).Item(0)), MISSING),
    IIF (NonEmptyCount (DESCENDANTS([Year].CurrentMember,10,LEAVES), [Time Series].[Per]) > 0,
    ([Time Series].[Per], Tail (Filter (DESCENDANTS([Year].CurrentMember,10,LEAVES),
    Not IsEmpty ([Time Series].[Per]))).Item(0).Item(0)),
    IIF (NonEmptyCount ([Year].levels(0).members, [Time Series].[Per]) > 0,
    ([Time Series].[Per], Tail (Filter ([Year].levels(0).members,
    Not IsEmpty ([Time Series].[Per]))).Item(0).Item(0)),MISSING))))
    ELSE SUM(PeriodsToDate([Year].Generations(1), [Year].CurrentMember), [Time Series].[Per] ) END