A Different Approach To Time Calculations in SSAS
A Different Approach To Time Calculations in SSAS
A Different Approach To Time Calculations in SSAS
David Shroyer OLAP Business Solutions April 2007 (Revised) Applies to: Microsoft SQL Server 2005 Analysis Services SP2 Summary: Analysis Services 2005 now includes built-in time intelligence enhancements which make it quick and easy to add time calculations to OLAP cubes. This paper will discuss the problems associated with the built-in enhancements and suggest a different approach for implementing global time calculations. Included with this paper is a .zip file called TimeCalculations.zip which includes the sample data mart and example SSAS project files. Contents Introduction The Business Problem The Built-In Time Intelligence Wizard Built-In Time Calculations Seen From a User Perspective Advantages to Using Built-In Time Intelligence Disadvantages to Using Built-In Time Intelligence How Does the Time Calculation Dimension Work? Creating a Shell Dimension Adding the Time Calculation Dimension to the Cube Adding the Time Calculations to the Cube Verifying the Prior Year Calculation Verifying the Year to Date Calculations Adding a Different Type of User Defined Hierarchy Adding the Attributes to the Dimension Adding the Calculations Conclusion
Introduction
AS2K5 offers developers a built-in wizard which will automatically add global time calculations to SSAS cubes. These built-in MDX scripts are very limited in their calculation scope and prove to be cumbersome for end-users to use. Fortunately it is very easy to create your own time calculation dimension which can then be customized to the applications requirements.
The fictitious company used in this example is called Sub Palace. Sub Palace is a small retail sandwich company headquartered in Northern California. The company has small retail outlets which have a limited sandwich and soda menu. The company has had some growth in the past few years and now has 10 retail outlets. The owner, Mr. Hanson, is trying to get a handle on his sales performance for each of his outlets. A development SSAS cube was built which contains sales and profitability data for his ten stores. The cube has the following dimensions: Products Promotions Stores Time Measures
www.obs3.com Page 1 of 22
The requirements for the cube include providing users the ability to track the various cube measures by year over year performance and year to date performance. Included in the Timecalculations.zip file is a back up copy of the Sub Palace Data Mart. If you want to follow along with the examples in the paper, you will need to restore this SQL Server 2005 database. There are also three SSAS projects located in the .zip file.
SubPalaceBase This is the original starting point cube. If you want to follow along with the examples, this is the cube that you will want to use. SubPalaceWizard This is the completed cube which uses the built-in time intelligence wizard. (First portion of the paper) SubPalaceManual This is the completed cube which contains a manually created time dimension. (Solution suggested)
This screen shot illustrates how a user interacts with the dimensions in a front end tool such as ProClarity. Note: To segment the attribute dimensions and hierarchies into folders, assign a value to the AttributeHierarchyDisplayFolder property of the attribute dimension. The key attribute dimension has been hidden to the user in the above example.
www.obs3.com
Page 2 of 22
Adding Time Intelligence Using the Built-In Wizard We are now going to use the Add Business Intelligence wizard to add some time intelligence to the cube. (A copy of this completed project is located in the accompanying zip file. The project name is SubPalaceWizard.) In this example we are interested in year over year calculations and year to date calculations. Double click on the Sales Cube to open the cube designer. Click the Add Business Intelligence button.
Click past the Wizard Welcome screen. For Cube Enhancement choose the first option which should be Define Time Intelligence. In the top drop down list choose: o o o o o o o o o Year to Date Year Over Year Growth % Year Over Year Growth Quarter Over Quarter Growth % Quarter Over Quarter Growth Month Over Month Growth % Month Over Month Growth Day Over Day Growth % Day Over Day Growth
Click Next
www.obs3.com Page 3 of 22
On the Define Scope Calculations click on the Select All tab. (Because in this case we want the time calculations to apply to all of the measures.)
What Did the Wizard actually do? 1. Added a new attribute dimension - If you now open up the Time dimension you will notice a new attribute dimension called Calendar Hierarchy Time Calculations. This shell dimensions is what anchors all of the time calculations. If you click on the Browser tab and select this dimension in the drop down box you will notice that this new attribute dimension has one new member called Current Time. 2. Added a field in the data source view (DSV) The new attribute dimension is using a new key that was added to the DimTime table in the DSV. If you open the DSV you will notice a new named calculation called Calendar Hierarchy Time Calculations. If you double click on the named calculation you will see the value as Current Time. This is the value that appears in the dimension as the base member.
www.obs3.com
Page 4 of 22
3. New calculation scripts were added New calculated members were added to the cube which performs the calculations that were selected in the wizard. Click on the Calculations tab. Click on the Script View icon and notice that there are now a bunch of MDX scripts which define the requested calculations. The first time we stepped through the time intelligence wizard we selected the Calendar Hierarchy. We also want to provide the same calculations for the Fiscal Hierarchy. Step through the wizard one more time, but this time choose the Fiscal Hierarchy. Notice that all of the same changes are made. There is new field created in the DSV, a new attribute dimension created in the time dimension and more MDX scripts added to the cube calculation script. Save and Reprocess the cube.
www.obs3.com
Page 5 of 22
Now lets see how these calculations work.. The first calculation we will check is the Year over Year Growth calculations. For this example we put the Calendar Hierarchy dimension on the rows and the Calendar Hierarchy Time Calculations dimension on the columns.
The calculations work except that the percent measure does not check for nulls before attempting to divide. (1.#INF)
www.obs3.com
Page 6 of 22
This next view shows the Quarter over Quarter and Month over Month calculations. You will notice that the Quarter and Month calculations are level dependant. They only provide values at the appropriate levels. This can cause some real usability issues with users because they have to constantly be aware of what level in the time hierarchy they are displaying so they can pull over the appropriate time calculation. Not only is it confusing for users, but it also creates a hard to read grid because the calculations are located in different columns for different levels.
www.obs3.com
Page 7 of 22
([Time].[Calendar Hierarchy].CurrentMember.Lag(1), [Time Calculations].[Current Period]), (ParallelPeriod([Time].[Calendar Hierarchy].[Year], 1), [Time Calculations].[Current Period]))
This formula now is saying Go back one year from the current period. Because we are no longer specifying a particular measure, the calculation will calculate for all measures. The trick here is to change the anchor of the formula from a measure to the base member of the shell dimension. This new calculation also has to reside on the shell dimension and not on the measures dimension.
user standpoint. (And it doesnt affect cube performance.) Even though we are going to create a physically separate shell dimension, we are going to build the new dimension from our time dimension table. (Like the wizard) Modifying the Time Dimension Table In this example, we are going to add a named calculation to the DimTime table which will be the source for our new Time Calculations dimension. Open the Data Source View Right click on the DimTime table and choose Named Calculation Name the column PeriodID The Expression equals NCurrent Period This places a column in the DimTime table where each record contains Current Period as the value for the PeriodID Click OK (see screenshot below)
Creating the TimeCalculation Dimension Right click on the Dimension folder, within the Solution Explorer, and choose New Dimension. Skip past the welcome screen by choosing Next Uncheck the Auto Build option and click Next Select the Data Source View SubPalace Select Standard Dimension when prompted for a Dimension Type. Click Next The Select the Main Dimension Table screen should have the DimTime selected as the main table. The key column is the PeriodId field and the member name column should be the PeriodID field. (As shown below)
www.obs3.com
Page 9 of 22
Click Next and then Next again to get to the last screen Name the Dimension Time Calculations and click OK Rename the Dim Time Calculations attribute dimension to Time Calculations
Because this dimension is going to be used exclusively for calculations, we do not want to have an All level. It wouldnt make sense to have a total for current period + prior year + ytd, etc. Click on the Time Calculations Attribute dimension and set the property IsAggregatable equal to False. (Shown below) This will not create an All Level.
www.obs3.com
Page 10 of 22
Save and Process the dimension. When you browse the dimension you should see one member called Current Period and no total at the top.
www.obs3.com
Page 11 of 22
Because this new time calculation dimension does not have any user defined hierarchies, the MDX will be simplified if we do not include the dimension name when referring to the dimension in MDX. Click on the Cube Structure tab. Highlight the Time Calculation Hierarchy in the lower left hand corner of the screen. Set the HierarchyUniqueNameStyle property to ExcludeDimensionName.
www.obs3.com
Page 12 of 22
Click on the Script View icon. Use the MDX below to create the calculations. These CREATE statements will create the calculations.
-- Time Series Calculations using the SCOPE command to fix on a sub cube -- Create the calculated members CREATE MEMBER CURRENTCUBE.[Time Calculations].[Prior Year] AS Null; CREATE MEMBER CURRENTCUBE.[Time Calculations].[Prior Year Chg] AS Null; CREATE MEMBER CURRENTCUBE.[Time Calculations].[Prior Year Chg %] AS IIF([Time Calculations].[Prior Year]=0,NULL, [Time Calculations].[Prior Year Chg] / [Time Calculations].[Prior Year]), FORMAT_STRING="Percent"; CREATE MEMBER CURRENTCUBE.[Time Calculations].[Prior Period] AS Null; CREATE MEMBER CURRENTCUBE.[Time Calculations].[Prior Period Chg] AS Null; CREATE MEMBER CURRENTCUBE.[Time Calculations].[Prior Period Chg %] AS IIF([Time Calculations].[Prior Period]=0,NULL, [Time Calculations].[Prior Period Chg] / [Time Calculations].[Prior Period]), FORMAT_STRING="Percent"; CREATE MEMBER CURRENTCUBE.[Time Calculations].[YTD] AS Null; CREATE MEMBER CURRENTCUBE.[Time Calculations].[YTD Pr Yr] AS Null; CREATE MEMBER CURRENTCUBE.[Time Calculations].[YTD Pr Yr Chg] AS Null; CREATE MEMBER CURRENTCUBE.[Time Calculations].[YTD Pr Yr Chg %] AS IIF([Time Calculations].[YTD Pr Yr]=0,NULL, [Time Calculations].[YTD Pr Yr Chg] / [Time Calculations].[YTD Pr Yr]), FORMAT_STRING="Percent"; The first thing to notice is that this calculation is being stored on the Time Calculation dimension, not the Measures dimension. CREATE MEMBER CURRENTCUBE.[Time Calculations].[Prior Year] The next step is to create the script which actually performs the time calculations. Add the MDX below to the calculation script AFTER the above create commands. The first Scope command allows the time calculations to only apply to a subset of the measures. Because the other Scope commands are nested within this one command, this approach allows for ease of maintenance because the logic is only listed in one location. The following script will limit the time calculations so they do not include the Markup calculated measure. The Scope function does not include the measure Markup, therefore the time calculations will return NULL when Markup is selected. -- Scope for the specific Measures -- You can use the commented out approach if you want to specify the exact measures you -- want included in the calculations. --Scope({[Measures].[Sales Amount], -- [Measures].[Cost Amount], -- [Measures].[Qty], -- [Measures].[Gross Margin Amount]}); --If you use this scope statement you are saying that yoiu want ALL of the measures
OLAP Business Solutions www.obs3.com Page 13 of 22
-- from the Fact Sales measure group EXCEPT the Markup Measure -- If in the future you add a new measure to the measure group, it will -- automatically be included Scope(MeasureGroupMeasures("Fact Sales") - {[Measures].[Markup]}); -- Calendar Time Hierarchy -- This piece scopes on the calendar hierarchy by scoping on the highest level (Year) and the key -- Because the calendar hierarchy attributes are related, this includes all members of that user defined hierarchy -- This is faster than scoping on the actual user defined hierarchy -- This approach only works if non of the attribute dimensions cross (or shared) between hierarchies. Scope([Time].[Calendar Year].[Calendar Year].members,[Time].[TimeKey].members); -- PRIOR YEAR CALCULATIONS ([Time Calculations].[Prior Year]= (ParallelPeriod([Time].[Calendar Hierarchy].[Year], 1, [Time].[Calendar Hierarchy].currentmember) ,[Time Calculations].[Current Period]) ); -- PRIOR PERIOD CALCULATIONS ([Time Calculations].[Prior Period]= IIF(([Time].[Calendar Hierarchy].currentmember.lag(1),[Time Calculations].[Current Period])=0,null, ([Time].[Calendar Hierarchy].currentmember.lag(1), [Time Calculations].[Current Period])) ); -- YTD CALCULATIONS ([Time Calculations].[YTD]= Aggregate( CrossJoin({[Time Calculations].[Current Period]}, PeriodsToDate([Time].[Calendar Hierarchy].[Year], [Time].[Calendar Hierarchy].CurrentMember)) ) ); -- YTD PRIOR YEAR ([Time Calculations].[YTD Pr Yr]= Aggregate( Crossjoin({[Time Calculations].[Current Period]}, PeriodsToDate( [Time].[Calendar Hierarchy].[Year], ParallelPeriod( [Time].[Calendar Hierarchy].[Year],1, [Time].[Calendar Hierarchy].CurrentMember)) ) ); End Scope; -- Fiscal Time Hierarchy Scope([Time].[Fiscal Year].[Fiscal Year].members, [Time].[TimeKey].members); -- PRIOR YEAR CALCULATIONS ([Time Calculations].[Prior Year]= (ParallelPeriod([Time].[Fiscal Hierarchy].[Fiscal Year], 1, [Time].[Fiscal Hierarchy].CurrentMember) ,[Time Calculations].[Current Period]) ); -- PRIOR PERIOD CALCULATIONS ([Time Calculations].[Prior Period]= IIF(([Time].[Fiscal Hierarchy].currentmember.lag(1),
OLAP Business Solutions www.obs3.com Page 14 of 22
[Time Calculations].[Current Period]) =0,null, ([Time].[Fiscal Hierarchy].currentmember.lag(1), [Time Calculations].[Current Period])) ); -- YTD CALCULATIONS ([Time Calculations].[YTD]= Aggregate( CrossJoin({[Time Calculations].&[Current Period]}, PeriodsToDate([Time].[Fiscal Hierarchy].[Fiscal Year], [Time].[Fiscal Hierarchy].CurrentMember)) ) ); -- YTD PRIOR YEAR CALCULATIONS ([Time Calculations].[YTD Pr Yr]= Aggregate( CrossJoin({[Time Calculations].[Current Period]}, PeriodsToDate( [Time].[Fiscal Hierarchy].[Fiscal Year], ParallelPeriod( [Time].[Fiscal Hierarchy].[Fiscal Year],1, [Time].[Fiscal Hierarchy].CurrentMember))) ) ); End Scope;
-- Now do the Variances which do not depend on a particular hierarchy -- Prior Year Chg ([Time Calculations].[Prior Year Chg]= [Time Calculations].[Current Period] - [Time Calculations].[Prior Year]); -- Prior Period Chg ([Time Calculations].[Prior Period Chg] = [Time Calculations].[Current Period] - [Time Calculations].[Prior Period]); -- YTD Prior Year Chg ([Time Calculations].[YTD Pr Yr Chg] = [Time Calculations].[YTD] - [Time Calculations].[YTD Pr Yr]); End scope; This approach assumes that users will NOT be using both user defined hierarchies at the same time. The rule for users of the cube is that they can only use one user defined time hierarchy at a time. But what this approach allows is one set of calculations that apply to both user defined hierarchies. Save and Deploy the Cube
www.obs3.com
Page 15 of 22
The Prior Year calculation is working at both the year and quarter level of the calendar hierarchy. In fact, if you drill down you will notice that it works at all levels within the cube. Now place the Fiscal Hierarchy on the Rows and move the Calendar Hierarchy to the background. Remember to select from the ALL level on the Calendar Hierarchy since the rule is that we can only use one at a time.
The screen shot above shows that the Prior Year calculation also calculates properly when the Fiscal Hierarchy dimension is displayed.
A calculation that was added in the manual creation of the time calculaion dimension that was not offered in the canned time intelligence wizard was the Prior Year calculation. Variances between the Current Year and Prior Year are important, but it is also valuable to display the Prior Year. The graph below shows a ProClarity view which has the Time Calculation dimension on the rows and the Calendar Hierarchy on the columns. The Prior Year calculation allows for the creation of powerful year over year visual comparisons. Notice how in this simple view, the user can see the Current Period, the Prior Year period, and the percent change all in one view.
OLAP Business Solutions www.obs3.com Page 16 of 22
The screenshot above validates that the YTD calculations are working properly. The YTD Pr Yr calculation is another very powerful calculation because it allows analysts to create charts like the one shown below.
www.obs3.com
Page 17 of 22
Another separate attribute dimension called Fiscal Years could be created which works in tandem with the user defined time periods hierarchy. This allows users to put the hierarchy on the rows, and thus have the ability to drill down, while the years are put on the columns. These two items can be put into a folder so users can easily see that they work together. The grid below shows an example grid with the fiscal time periods hierarchy on the rows and the fiscal years attribute dimension on the columns. Also nested on the columns is the Time Calculations dimension.
www.obs3.com
Page 18 of 22
4. 5. 6. 7.
Set the allmembername of the hierarchy to Year Total Set the Fiscal Month in Year attribute to Sort using the Month Sort attribute. Hide the fiscal qtr in year, fiscal month in year, and fiscal week in year attributes. Put the Fiscal Time Periods hierarchy and the Fiscal Years attribute in a folder called Fiscal Attributes 8. Save and Process the dimension.
-- Fiscal Time Periods Hierarchy Scope([Time].[Fiscal Years].[Fiscal Years].members, [Time].[TimeKey].members); -- YTD Calculation ([Time Calculations].[YTD]= Aggregate( CrossJoin({[Time Calculations].[Current Period]}, {NULL:[Time].[Fiscal Time Periods].CurrentMember}) ) ); -- PRIOR PERIOD CALCULATIONS ([Time Calculations].[Prior Period]= IIF(([Time].[Fiscal Time Periods].currentmember.lag(1), [Time Calculations].[Current Period])=0,null, ([Time].[Fiscal Time Periods].currentmember.lag(1), [Time Calculations].[Current Period])) ); -- Scope for Qtr1 Scope([Time].[Fiscal Qtr in Year].[Q1]); ([Time Calculations].[Prior Period]= ([Time].[Fiscal Qtr in Year].[Q4], [Time].[Fiscal Years].prevmember,[Time Calculations].[Current Period])); End Scope; -- Scope for JUL Scope([Time].[Fiscal Month in Year].[Jul]); ([Time Calculations].[Prior Period]= ([Time].[Fiscal Month in Year].[Jun], [Time].[Fiscal Years].prevmember,[Time Calculations].[Current Period])); End Scope; -- PRIOR YEAR CALCULATIONS ([Time Calculations].[Prior Year]= ([Time].[Fiscal Years].prevmember ,[Time Calculations].[Current Period]) ); -- YTD LY ([Time Calculations].[YTD Pr Yr]= Aggregate( CrossJoin({[Time Calculations].[Current Period]}, {NULL:[Time].[Fiscal Time Periods].CurrentMember}, {[Time].[Fiscal Years].prevmember}) ) ); End Scope; After saving and deploying the project you should be able to see the new folder under the Time dimension. Within the folder you should see the Fiscal Time Periods hierarchy and the Fiscal Years attribute. Now users can use the following combinations with the Time Calculations dimension: Calendar Hierarchy with Time Calculations Dimension Fiscal Hierarchy with the Time Calculations Dimension Fiscal Time Periods Hierarchy AND Fiscal Years Attribute with the Time Calculations Dimension. Day of Week Attribute with any of the above.
OLAP Business Solutions www.obs3.com Page 20 of 22
The graph and grid below shows the type of analysis than can now be accomplished with the new Fiscal Time Periods hierarchy.
Users can also use the Day of Week attribute with the Fiscal Time Periods hierarchy to see data at the day level. This is accomplished by nesting the Fiscal Time Periods and the Day of Week dimensions on the rows.
www.obs3.com
Page 21 of 22
Conclusion
SSAS now has built-in time intelligence which developers can use to quickly and easily create time calculations. But manually creating shell time dimensions still offer the greatest flexibility when it comes to implementing global time calculations. Shell dimensions can also be used for other global type of calculations, such as mean, median, mode, etc. Support Files: Sample Database and Project Files Other Resources: Defining Time Intelligence (SSAS) Time Intelligence Wizard Fix (If you dont have SP1)
www.obs3.com
Page 22 of 22