Skip to main content

Recursive Report to Traverse Time Hierarchy

I need to create a report that allows end-users to select multiple items from the Date hierarchy (e.g., Year-2007, Q4-Year-2006, November-2007) and then the report should show a chart and a table with measures for the selected periods like this one (using the Adventure Works cube from SQL 2012)

And the next requirement is to allow users to click on the date in the table (e.g., Q4 CY 2006) and the reports will show the same information but for all direct children of the selected time period  (e.g., Oct 2006, Nov 2006, Dec 2006) like this one:


And if a user clicks on the month it will get the report for all dates in the selected month.

I started with Query designer and placed "Internet Sales Amount", Category (from the Product dimension) , and the Date.Calendar hierarchy. I also add the Date.Calendar hierarchy to the parameters panel.



When I changed the design mode the MDX script generated by the Query Designer is this (slightly reformatted)

SELECT 
NON EMPTY { [Measures].[Internet Sales Amount] } ON COLUMNS, 

NON EMPTY { ([Product].[Category].[Category].ALLMEMBERS * 
     [Date].[Calendar].[Date].ALLMEMBERS ) } 
DIMENSION PROPERTIES MEMBER_CAPTION, MEMBER_UNIQUE_NAME ON ROWS 

FROM ( SELECT ( STRTOSET(@DateCalendar, CONSTRAINED) ) ON COLUMNS 

FROM [Adventure Works]) 
CELL PROPERTIES VALUE, BACK_COLOR, FORE_COLOR, FORMATTED_VALUE, FORMAT_STRING, FONT_NAME, FONT_SIZE, FONT_FLAGS


So the @DateCalendar parameter contains the initial selection made by the user using the unique names for each item.

When a user clicks on a date in the table we need to call the same report and pass it the unique name of the selected date item.
Also we need a way to find out names for the dates.
And we do not need all dates (as crossjoin with[Date].[Calendar].[Date].ALLMEMBERS) defines.

So we will modify the MDX query by adding two calculated members for the Unique name and Member name, and instead of crossjoin with all the dates we will crossjoin with StrToSet(@DateCalendar)



WITH 
MEMBER [Measures].[DateName] AS [Date].[Calendar].CurrentMember.member_caption
MEMBER [Measures].[UName] AS [Date].[Calendar].CurrentMember.UniqueName

SELECT 
NON EMPTY {  [Measures].[Internet Sales Amount], [Measures].[DateName], [Measures].[UName]
} ON COLUMNS, 
NON EMPTY { 
(  [Product].[Product Categories].[Category].ALLMEMBERS *  StrToSet(@DateCalendar) )

DIMENSION PROPERTIES MEMBER_CAPTION, MEMBER_UNIQUE_NAME ON ROWS 
FROM [Adventure Works]
CELL PROPERTIES VALUE, BACK_COLOR, FORE_COLOR, FORMATTED_VALUE, FORMAT_STRING, FONT_NAME, FONT_SIZE, FONT_FLAGS

The returned amount of data is much smaller now and two new columns (dateName and Uname) will be used in the report



Now when the report is initially launched by the user we need to crossjoin products with StrToSet(@DateCalendar).

But when the user clicks on a date in the table we'll pass a unique name for this member and we need to show all children for the member.
So we need crossjoin with
         StrToSet(@dateCalendar + '.Children")

But when we reached the date level in the date hierarchy we need again crossjoin with
                     StrToSet(@DateCalendar)

Thus, we need a new hidden report parameter (let's call it @ShowChildren) with type Boolean and the default value set to FALSE



Now we can add this report parameter as a parameter to the MDX query


And having the parameter ready the MDX query itself will change as this


WITH 
MEMBER [Measures].[DateName] AS [Date].[Calendar].CurrentMember.member_caption
MEMBER [Measures].[UName] AS [Date].[Calendar].CurrentMember.UniqueName
SELECT 
NON EMPTY {  [Measures].[Internet Sales Amount], [Measures].[DateName], [Measures].[UName]
} ON COLUMNS, 
NON EMPTY { 
(  [Product].[Product Categories].[Category].ALLMEMBERS *  
-- StrToSet(@DateCalendar) 
iif (  @ShowChildren AND StrToSet(@DateCalendar).Count=1 And  NOT IsLeaf(StrToMember(@DateCalendar)), StrToSet(@DateCalendar + ".Children"),  StrToSet(@DateCalendar))

)

DIMENSION PROPERTIES MEMBER_CAPTION, MEMBER_UNIQUE_NAME ON ROWS 
FROM [Adventure Works]
CELL PROPERTIES VALUE, BACK_COLOR, FORE_COLOR, FORMATTED_VALUE, FORMAT_STRING, FONT_NAME, FONT_SIZE, FONT_FLAGS

Now the last step is to define action on the text box with the date name in the report.
We call the report itself (Report1) and pass to it two parameters:
- DateCalendar with value from the [Uname] field
- ShowChildren with value TRUE


That's all we need. 

Now the report when called initially will get date periods as selected by the user (it might be one or multiple items but in any case the Internet Sales amount will be shown for the selected dates only).
When the user clicks on any date in the table the report will show the Internet Sales amount for the direct children of the data user clicked on.
When we reach the bottom of the hierarchy (e.g., January 18, 2007) and user clicks again on the date the report will show the same information because that member is a leaf member in the hierarchy.

And just for completeness, the category group in the chart is [DateName] and Series Group is [Category]



Comments

Popular posts from this blog

Joining Windows 10 to Azure AD Domain

As of October 2016 to join Windows 10 computers to Azure AD Domain service requires these steps: Create a VNET in the classic portal . The VNET must be placed to a region where Azure AD domain service is available (( https://azure.microsoft.com/en-us/regions/services/ )  In the classic portal  go to Directory -> Configure and enable the domain service. And wait for ~ 30 min When completed the IP address will be populated Go back to the VNET configuration and add a DNS server with the IP (10.0.0.4 in this case) Create the "AAD DC Administrator" administrators group (again in Directory -> Group). Members of this group are granted administrative privileges on machines that are domain-joined to the Azure AD Domain Services managed domain. Add to the group your users who are supposed to have the administrative access on a Windows 10 computer go to Settings -> Accounts (this is true for Windows 10 version  1607) then select 'Access work

Create 3-Node Windows 2012 Multi-subnet Cluster

Environment There are two Data centers connected via a WAN link. Two Windows 2012 Servers (called SQLDEV1 and SQLDEV2) are located in the Primary Data Center (on the IP subnet 192.168.79.0/24) and the third server is placed in the Secondary Data Center with the 192.168.69.0/24 subnet. We’ll be creating a three-node Windows cluster with no shared storage on the multi subnet network with a file share witness at the Primary Data Center. We’ll be using a file share witness to protect from the cluster failure in a situation when the network between the Data Centers is unavailable and one of the servers in the Primary Data Center is also down (or being rebooted). The final state will look like depicted above: -           Two Virtual IP’s will be assigned (192.168.76.218 and 192.168.69.134) to the cluster -           The servers at the Primary Data Center will have a vote (Vote=1) and the server at the Secondary Data Center will have no vote (Vote=0). The file share witness al

Generate Calendar Table in Power BI with Fiscal Year Attributes

In Power BI go to Get Data --> Blank Query and paste into the Function windows the text below. This function takes as parameters: - StartYear (e.g., 2012) - EndYear (e.g., 2018) -FiscalYearStartMonth (e.g., 4) And it will generate a calendar table for dates from Jan-1-<StartYear> till Dec-31-<EndYear> and columns for Fiscal Year, Fiscal Month, Fiscal Quarter, Fiscal Year Day where the Fiscal year begins on FiscalYearStartMonth = (StartYear as number, EndYear as number, FiscalYearStartMonth as number)=> let     //Capture the date range from the parameters     StartDate = #date(StartYear, 1, 1),     EndDate = #date(EndYear, 12, 31), //Get the number of dates that will be required for the table     GetDateCount = Duration.Days(EndDate - StartDate)+1, //Take the count of dates and turn it into a list of dates     GetDateList = List.Dates(StartDate, GetDateCount,     #duration(1,0,0,0)), //Convert the list into a table     DateListToTable