Drillthrough On Calculated Members In SSAS MD 2017 Using DAX Expressions, Part 1
Reposted from Chris Webb's blog with the author's permission.
Without a doubt one of the most useful features of SSAS Tabular 2017 is the new Detail Rows Expression property. It allows you to control exactly which columns and rows appear when you do a drillthrough – something that is particular important when you’re doing a drillthrough on a calculation, and something that SSAS MD users have also wanted to have for a long time now. For example, imagine that you have an Excel PivotTable that is sliced by a single date and a calculated member that shows a year-to-date sum of sales calculation: when a user does a drillthough they would expect to see data for all the fact data that contributes to the value they have clicked on, which in this case means data for all dates from the beginning of the year up to the selected date; this is what the Detail Rows Expression property makes possible and this is exactly what a regular drillthrough in SSAS MD doesn’t do.
There have been many attempts at solving this problem in SSAS MD, from Mosha’s blog post back in 2008 to these custom functions in the Analysis Services Stored Procedure Project (for a few more weeks still on Codeplex, but when Codeplex dies available here on GitHub). None of these solutions have been perfect and all have involved a certain amount of .NET code. In this series of posts I’m going to describe a slightly different approach, and while it isn’t perfect either and is very complex (you’ll need to be good at MDX and DAX to implement it) I think it has a lot to recommend it, not least because no .NET code is required. In this first post I’m going to demonstrate some of the functionality that makes my approach possible; in part 2 I’ll put it all together into a working solution.
First thing to note: you have been able to query SSAS MD using DAX as well as MDX since SQL Server 2012 SP1 CU3. Most client tools, like Excel, generate MDX queries but Power BI for example generates DAX queries when you create a Live connection to SSAS MD. To learn more about DAX support in SSAS MD this video of a session of mine from SQLBits from a few years ago is a good place to start; it’s fairly old but most of the content is still relevant.
This in turn means that you can create a Rowset action (not a Drillthrough action) in SSAS MD that return the results of a DAX query. Here’s an example of an action that does this:
The Action Expression property is an MDX expression that returns the text of the query to be executed and whose results will be displayed to the user as the output of the drillthrough. In this case the MDX expression consists of one string, and that string is a DAX query that returns a list of Sales Order Numbers, Line Numbers and their associated Sales Amounts:
EVALUATE SELECTCOLUMNS( 'Sales Order', "Sales Order Number", 'Sales Order'[Sales Order Number], "Sales Order Line Number", 'Sales Order'[Sales Order Line Number], "Sales Amount", [Sales Amount])
Here’s the result in Excel:
This is just a static query though, and for an action you will need to generate a query dynamically to return an appropriate table of data depending on which cell the user has drilled through on.
However before I carry on there’s an important question that needs to be addressed. You may be wondering why I’m using a DAX query for this, when I could be using an MDX DRILLTHROUGH statement (as in the approaches linked to above) or an MDX SELECT statement. The problem with a DRILLTHROUGH statement is that it can only accept an MDX SELECT statement that returns a single cell in its first parameter; this means it’s not possible to get it to return more complex resultsets like the one required for the year-to-date example above. Normal MDX SELECT statements don’t suffer from this restriction and it would indeed be possible to dynamically generate one that meets any need. Unfortunately when the results of an MDX SELECT statement are returned from a Rowset action you have no control over the format of the column headers that are returned, and they are often not pretty at all. A DAX query, in contrast, gives you complete control over the data that is returned and the way the column headers are formatted.
The last question I’m going to address in this post is how the DAX query can be made dynamic. To do this I’m going to use the new DAX IN operator, which is only available in SSAS 2017. As always with DAX, there’s a great article describing it written by Marco Russo here:
https://www.sqlbi.com/articles/the-in-operator-in-dax/
Here’s how the DAX query above can be adapted to return the Sales Orders for just two dates using the IN operator:
EVALUATE FILTER( CALCULATETABLE( SELECTCOLUMNS( 'Sales Order', "Sales Order Number", 'Sales Order'[Sales Order Number], "Sales Order Line Number", 'Sales Order'[Sales Order Line Number], "Sales Amount", [Sales Amount]), 'Date'[Date.Key0] IN {20030101, 20030102}), [Sales Amount]>0)
In this example, the ‘Date’[Date.Key0] column is the column that contains the key values of the Date attribute on the Date dimension in my SSAS cube. To make this dynamic, you need an MDX expression that will return a query like the one above and, in particular, return a different list of date keys depending on what the user has drilled through on. The MDX GENERATE() function can be used to do this: you can use it to iterate over the set of existing members on the Date attribute of the Date dimension and output a comma-delimited list of key values from each member:
"EVALUATE FILTER( CALCULATETABLE( SELECTCOLUMNS( 'Sales Order', ""Sales Order Number"", 'Sales Order'[Sales Order Number], ""Sales Order Line Number"", 'Sales Order'[Sales Order Line Number], ""Sales Amount"", [Sales Amount]), 'Date'[Date.Key0] IN {" + GENERATE(EXISTING [Date].[Date].[Date].MEMBERS, [Date].[Date].CURRENTMEMBER.PROPERTIES("KEY"), ",") + "}), [Sales Amount]>0)"
If this expression is used in an action and a user drills down on, say, the month April 2003, the following DAX query is generated and run to get all the Sales Orders for all the days in April 2003:
EVALUATE FILTER( CALCULATETABLE( SELECTCOLUMNS( 'Sales Order', "Sales Order Number", 'Sales Order'[Sales Order Number], "Sales Order Line Number", 'Sales Order'[Sales Order Line Number], "Sales Amount", [Sales Amount]), 'Date'[Date.Key0] IN {20030401,20030402,20030403,20030404,20030405, 20030406,20030407,20030408,20030409,20030410,20030411, 20030412,20030413,20030414,20030415,20030416,20030417, 20030418,20030419,20030420,20030421,20030422,20030423, 20030424,20030425,20030426,20030427,20030428,20030429, 20030430}) , [Sales Amount]>0)
OK, that’s more than enough for one post. In my next post I’m going to look at some of the shortcomings of this approach, how they can be (partly) worked around, and demonstrate a full solution for drillthrough on a regular measure and also on a year-to-date calculation.
Chris has been working with Microsoft BI tools since he started using beta 3 of OLAP Services back in the late 90s. Since then he has worked with Analysis Services in a number of roles (including three years spent with Microsoft Consulting Services) and he is now an independent consultant specialising in complex MDX, Analysis Services cube design and Analysis Services query performance problems. His company website can be found at http://www.crossjoin.co.uk and his blog can be found at http://cwebbbi.wordpress.com/ . |
Tags: drillthrough, dax, tabular