Wednesday, August 17, 2016

Cool Essbase MDX Stuff – Dimension Properties Edition

I have always liked MDX as it exposes very powerful operations for creating member sets.  It has this amazing ability to take a set of members, union it with another set of members, intersect it with yet another set of members, and then exclude members from yet another set.  This is very powerful.

Recently, I was talking with a customer about some creative uses for MDX and they told me about some cool things they had done with MDX.  Kudos to George Cooper and Esam Jaber at Gap for showing me some creative MDX.  Though this is a really cool and new-to-me technique, after doing some online searches I found that the technique isn’t new to everyone.  Both Gary Crisci and Harry Gates have blogged on it already at http://garycris.blogspot.com/2014/03/mdx-queries-to-get-parentchild-and.html and http://www.cubesavvy.com/cubesavvy-utilities-updated-mdx-capabilities, respectively.

This technique, which gives developers access to outline information that was not previously available, also seems to be lightning fast.  I decided maybe I need to write on it as well and, of course, put my own spin on it by showing how we can use this in the Dodeca Spreadsheet Management System.

So, what is it?  They are called dimension properties and can utilize the PROPERTY_EXPR function.  This function provides the ability to query for, and return, members related to a given member.  The ability to return related members, on the same row, is something that is unique to the Essbase query languages.  The Grid API, or query-by-example and used to retrieve data from Essbase in the classic add-in, Smart View, and Dodeca, cannot do it out of the box.  The Essbase Report Script language, which is frequently used for exporting data, cannot do it either.  Regardless, we have customers who have asked to how have the parent member and/or the grandparent member on the same row as a member.   In the remainder of this blog post, I will explain dimension properties, and more specifically, the PROPERTY_EXPR function.

For this post, I will use the Geography dimension of the ASOSamp.Sample database.  The Geography dimension features members from different regions of the United States including details of the Region, the State, the City, and the Postal Code.  Here is a screenshot of some members in the Geography dimension:

Geography dimension members

With this dimension, let’s suppose you would like to get outline information at the city level, or level 1 in Essbase-speak.  In addition, let’s say you would like to have the state and the region as well.   Finally, while we are at it, maybe you also want to know the generation number of the member.  With dimension properties and the PROPERTY_EXPR function, you can get all of that information in one trip to the server.  Here is the query:

Select {} on COLUMNS,
Hierarchize(Descendants(Geography, Geography.Levels(1), SELF_AND_BEFORE), POST)
DIMENSION PROPERTIES
 GEN_NUMBER,
  PROPERTY_EXPR(Geography, MEMBER_NAME, Ancestor(CurrentAxisMember(), Geography.Generations(2)),"Gen2"),
  PROPERTY_EXPR(Geography, MEMBER_NAME, Ancestor(CurrentAxisMember(), Geography.Generations(3)),"Gen3")
 ON ROWS
FROM ASOSamp.Sample

Let’s break down the query to examine the components.  First, the Column axis specification is simple:

Select {} on COLUMNS,

The Column axis specification contains an empty set, but why do you need to even specify a set at all?  The Essbase MDX specification states that a query cannot skip axes based on a set order.  It seems much more natural for me to get data back on the second axis, or the Row axis, and as the Column axis is the first specified axis, a query cannot skip the Column axis and specify a Row axis.  As we really aren’t looking for any data to be returned in this query, then we can just use an empty set.

Next, let’s look at the Row axis specification:

Hierarchize(Descendants(Geography, Geography.Levels(1), SELF_AND_BEFORE), POST)

Let’s split this into sections starting from the inside out.  The Descendants function returns, naturally, all of the descendants of a given member down to, and including, level 1 members in the outline.  For this database, this query will return the Geography dimension down to the City level, but will not return the bottom, or zip code, level.

The member set returned by the Descendants function is then sorted by the Hierarchize function.

Hierarchize(Descendants(Geography, Geography.Levels(1), SELF_AND_BEFORE), POST)

The POST argument specifies that child members are sorted before their parent as they are in the spreadsheet add-ins.

Next, the DIMENSION PROPERTIES modifier for the Row axis specification provides the ability to return additional outline information related to each member returned in the set.  The DIMENSION PROPERTIES specify that the generation number, the related generation 2 and generation 3 members are returned for each member in the set.

DIMENSION PROPERTIES
 GEN_NUMBER,
  PROPERTY_EXPR(Geography, MEMBER_NAME, Ancestor(CurrentAxisMember(), Geography.Generations(2)),"Gen2"),
  PROPERTY_EXPR(Geography, MEMBER_NAME, Ancestor(CurrentAxisMember(), Geography.Generations(3)),"Gen3")

The GEN_NUMBER argument is self-explanatory, so let’s look at the PROPERTY_EXPR function to see how it works.  Consider this portion of the statement:

PROPERTY_EXPR(Geography, MEMBER_NAME, Ancestor(CurrentAxisMember(), Geography.Generations(2)),"Gen2")

The first argument is the dimension name, so that is easy enough.

The second argument, called the property_name argument, has a number of valid values as documented by Oracle in the documentation.  Valid values are MEMBER_NAME, MEMBER_ALIAS, LEVEL_NUMBER, GEN_NUMBER, IS_EXPENSE, COMMENTS, RELATIONAL_DESCENDANTS, MEMBER_UNIQUE_NAME), an attribute dimension name, an alias-table name, or a UDA.  At the time of this writing, the current docs are located at http://docs.oracle.com/cd/E26232_01/doc.11122/esb_tech_ref/frameset.htm?mdx_property_expressions.html.

The third argument is called member_value_expression and this is where the magic happens.  In this argument, you can use a number of functions that return exactly one member that is related to the current member.  In my example above, the Ancestor function is being used to return, for each member in the set as specified by the CurrentAxisMember function, the ancestor of that member at a given generation.  Among other functions are functions that allow you to get the parent, first child, next sibling, or previous sibling of the current member.

The fourth and final argument is used to give a title to the column containing the extended information.

So, now that we know a bit about this syntax, what does it look like in EAS?  

MDX dimension properties in EAS
In their blogs, both Gary and Harry talk about ways to actually consume this information.  Harry wrote a special interface to display dimension properties returned by MDX.  Gary talked about the idea of using an Excel macro to parse the output.  I had my own ideas on how to use dimension properties in Dodeca.  I setup a simple Dodeca report and used a only 3 lines of our automation language, workbook scripting, to build the view.  Here is screenshot of my simple Dodeca view.

Dynamic MDX View in Dodeca
While running this view in Dodeca, the user can filter based on Geography, Product, and Stores dimensions and the report is highly dynamic.  The automation in Dodeca performs these tasks:
  1. Returns members and properties as the descendants of a user-selected Geography member
  2. Retrieves and places the dimension properties and the members on the worksheet
  3. Places the selected Product and Stores dimension members in the proper location
  4. Retrieves Essbase data into the worksheet
  5. Creates Excel grouping based on the generation number
I will leave the step-by-step of creating this view in Dodeca to another blog post.

So, how could you use dimension properties?


Sunday, August 7, 2016

ODTUG Leadership Program / ODTUG Board Elections

As many of you know, I have spent many years helping the Hyperion community in many ways.  I have wrote this blog for quite some time, have taken on the OlapUnderground Utilities and provided free support through my company,  In other words, I have worked hard to give back to the community and, in return, have had many kind words from people we have helped.

As a result of this spirit of giving back, I was lucky enough to be elected to the ODTUG Board of Directors which brings me to the real point of this blog post.  There is an upcoming ODTUG Board Election coming up and, due to term limits, my time on the board is coming to a close.  The same is true of my good friend Cameron Lackpour.  With two board seats open, could *you* be the next person to step up?

Here are my thoughts on what it takes to be a board member.  Joining the ODTUG Board isn't just something you decide to do and it isn't an achievement that is 'a feather in someone's hat'.  It is a commitment to helping others in the community for two years.  But it is more than that.  To be a successful ODTUG Board member, the commitment you have should be a continuation of the long-term commitment you have made to helping others.  So, if you are interested in serving on the ODTUG Board at some time in the future, the thing you should do is #GetInvolved.  If you are not already involved, the easiest way to get involved is to volunteer on the ODTUG Volunteer page.

If you are ready to step up from that point, for a number of years, ODTUG has maintained a Leadership Program to help train the next generation of users.  The application process for this year's Leadership Program is open for another week.  For more information, or to submit an application for the Leadership Program, you can get more information on the ODTUG Leadership Program page.