THE SQL Server Blog Spot on the Web
Welcome to SQLblog.com - The SQL Server blog spot on the web Sign in | Join | Help
in Search

Microsoft OLAP by Mosha Pasumansky

Drillthrough on calculated measures

Drillthrough is a great feature of Analysis Services, but it has a limitation – only cells without calculations covering them can be drilled through. This automatically rules out drillthrough for calculated members in general and calculated measures in particular. This limitation has been a subject to much debate in forums and blogs. It has been logged on connect site as “Enable drillthrough on calculated measures” suggestion, where it gathered strong support. And competition struggles with very similar issues as well.

While there is no universal solution to this problem, in this article we will show how many practical particular cases can be solved in AS2005. Let’s first understand what does it mean to drill through on calculated measure. According to the definition:

“Drillthrough is the operation in which a user specifies a single cell and the Analysis server returns the most detail level data that contributed to that cell.”

For the regular cells it is pretty straightforward to figure out which detailed level data contributed to them, but in case of arbitrary calculation, it can be very tricky. Basically, one needs to reverse engineer the expression of the calculation, find out all the dependencies, and drill through to each one of them. But in case of simple calculations, such dependencies are obvious. Let’s consider the [Internet Gross Profit] calculated measure from the Adventure Works cube. It is defined as

Create Member CurrentCube.[Measures].[Internet Gross Profit]

 AS [Measures].[Internet Sales Amount] - [Measures].[Internet Total Product Cost],
 
Format_String = "Currency";

It is obvious, that [Internet Gross Profit] is computed from two real measures: [Internet Sales Amount] and [Internet Total Product Cost]. But how can we convey this to the engine, since no matter what the expression of the calculated measure is, the DRILLTHROUGH statement is going to fail on it. The trick here is to build a DRILLTHROUGH statement which redirects from calculated measure to the real measures it derived from. Below are the exact steps in order to achieve that:

1. Define new action. Note, that it cannot be “Drillthrough action”, since it won’t even apply to calculated measures. Just a regular action.

2. Set the “Action Target” property to “Cells” – after all drillthrough is done on cells

3. Since the “Target Object” for cells actions is always “All cells”, we will use condition to constrain our action just to the desired calculated measure. In the “Condition” property set the following expression

Measures.CurrentMember IS [Measures].[Internet Gross Profit]

4. “Action Type” needs to be set to “Rowset”, since drillthrough returns rowset.

5. Set the “Caption” to “Drillthrough”, so in UI of the client tool it will show up with this title, and it will be obvious to the user.

6. “Action Expression” is the most important property. Here we need to build the MDX DRILLTHROUGH statement which redirects to two real measures. The MDX DRILLTHROUGH does not allow multiple cells to appear in the SELECT clause. So how will we get two measures ? We will use the fact that both of them belong to the same measure group, and instead of putting them into the SELECT clause, we will send them to RETURN clause. Building custom return clause also has a benefit of having flexibility to choose which attributes will show up in the result of drillthorugh. By default all the granularity attributes of the measure group will be there, but we can include less or more attributes. We also get to decide on the actual content in these attribute columns – whether we want keys, names, unique names etc. Here is how the RETURN clause may look like:

RETURN 
 [Internet Sales].[Internet Sales Amount]
,[Internet Sales].[Internet Total Product Cost]
,NAME([$Date].[Date])
,NAME([$Customer].[Customer])
,NAME([$Product].[Product])
,NAME([$Promotion].[Promotion])
,NAME([$Source Currency].[Source Currency Code])
,NAME([$Sales Reason].[Sales Reason])

In order for drillthrough to address the correct cell, we need to put current cell coordinates into the SELECT clause. One way of doing it would be to write long MDX string concatenation, using <hierarchy>.CurrentMember.UniqueName subexpression for every hierarchy in the cube. However, this is tedious and error-prone approach. There are hundreds of hierarchies, new ones can be added, old ones removed or renamed etc. Instead, we can just call stored procedure which will enumerate all the current coordinates (note that we could’ve done the same with the RETURN clause too).

Analysis Services Stored Procedures Project (ASSP) actually features some sprocs very similar to what we need here, under FindCurrentMembers class. Similar, but not quite what we need. In our particular case, we want to skip measures (because we are hardcoded them), and also for brevity, we need to consider only attribute hierarchies. Below source code achieves just that:

public static string CurrentCellAttributes()
{
    // start with empty string
    string coordinate = String.Empty;
    bool first = true;
    foreach (Dimension d in Context.CurrentCube.Dimensions)
    {
        // skip measures
        if (d.DimensionType == DimensionTypeEnum.Measure)
            continue;

        foreach (Hierarchy h in d.AttributeHierarchies)
        {
            // skip user hierarchies - consider attribute and parent-child hierarchies
            // (parent-child is both user and attribute hierarchy)
            if (h.HierarchyOrigin == HierarchyOrigin.UserHierarchy)
                continue;
            if (!first)
                coordinate += ",";
            first = false;
            coordinate += h.CurrentMember.UniqueName;
        }
    }

    return coordinate;
}

Now, equipped with this powerful stored procedure, we can build the expression for the action:

"DRILLTHROUGH MAXROWS 100 SELECT (" +
ASSP.ASStoredProcs.FindCurrentMembers.CurrentCellAttributes()
+ ") ON 0 FROM [Adventure Works] 
RETURN 
 [Internet Sales].[Internet Sales Amount]
,[Internet Sales].[Internet Total Product Cost]
,NAME([$Date].[Date])
,NAME([$Customer].[Customer])
,NAME([$Product].[Product])
,NAME([$Promotion].[Promotion])
,NAME([$Source Currency].[Source Currency Code])
,NAME([$Sales Reason].[Sales Reason])
"

We are done ! Now if we deploy this action, any client that supports actions (such as Excel 2007 or cube browser), on the right click on cell under [Internet Gross Profit] calculated measure, we will see additional menu item “Drillthrough”, and clicking on it will bring the most detail level data that contributed to that cell.


Published Monday, September 01, 2008 10:40 PM by mosha
Filed under: ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Chris Woolderink said:

This sounds really interesting. My ultimate goal is to use Drillthrough from a scorecard in PerformancePoint on a calculated member, so this post sounds promising! Thanks for sharing.

I followed the steps in your post, but when I right click on a cell that contains value for "Internet Gross Profit" in the Cube Browser of Analysis Services, then I can't select my action. So it doesn't appear. What could I have done wrong?

I used this code for the stored procedure, compiled it (after adding references to "Analysis Management Objects" and "Microsoft.AnalysisServices.AdomdServer") and referenced it by Right-clicking on Assemblies in Analysis Services:

using System;

using System.Collections.Generic;

using System.Text;

using Microsoft.AnalysisServices.AdomdServer;

namespace ASSP

{

   public static class ASStoredProcs

   {

       public static string CurrentCellAttributes()

       {

           // start with empty string

           string coordinate = String.Empty;

           bool first = true;

           foreach (Dimension d in Context.CurrentCube.Dimensions)

           {

               // skip measures

               if (d.DimensionType == DimensionTypeEnum.Measure)

                   continue;

               foreach (Hierarchy h in d.AttributeHierarchies)

               {

                   // skip user hierarchies - consider attribute and parent-child hierarchies

                   // (parent-child is both user and attribute hierarchy)

                   if (h.HierarchyOrigin == HierarchyOrigin.UserHierarchy)

                       continue;

                   if (!first)

                       coordinate += ",";

                   first = false;

                   coordinate += h.CurrentMember.UniqueName;

               }

           }

           return coordinate;

       }

   }

}

Thanks in advance for any ideas!

September 2, 2008 12:04 PM
 

mosha said:

Chris - start debugging this the same way you would do in any other scenario, i.e. start Profiler to intercept and inspect Discover for MDSCHEMA_ACTIONS, if the call is OK - put breakpoint in the sproc, etc.

September 2, 2008 12:16 PM
 

Chris Woolderink said:

Thanks for your tip. I found the problem: you have to use the assembly name when you are invoking it from the expression: ASSP.ASStoredProcs.FindCurrentMembers.CurrentCellAttributes(). My guess is that your assembly name is ASSP.ASStoredProcs.FindCurrentMembers.dll.

When I used my assembly name it worked fine!

The drillthrough was working from the cube browser. However, PerformancePoint doesn't support actions for scorecards. The Analytic Grid supports URL, Drillthrough and Reporting Services Action; your approach didn't work either, probably because it's a "rowset action".

Anyway thanks for sharing your approach, because if the client supports the action then it's very useful!!

Cheers,

Chris.

September 4, 2008 5:12 AM
 

Dip said:

Thanks for the solution, a basic question though, how do I create "CurrentCellAttribultes()" C# .dll file? Using Visual Studio I suppose?

September 27, 2008 11:24 PM
 

Infology.Ru » Blog Archive » Drillthrough ???? ?????????????????????? ?????????? said:

October 22, 2008 4:40 PM
 

TomVdP said:

Why not give an option to do the drillthrough on the "NON_EMPTY_BEHAVIOR measure" ?  I think that would be a good solution for straightforward calculated measures.

November 4, 2008 9:20 AM

Leave a Comment

(required) 
(optional)
(required) 
Submit
Powered by Community Server (Commercial Edition), by Telligent Systems
  Privacy Statement