CHAPTER 5
One of the benefits of using an Analysis Services database for reporting and analysis (instead of using a relational database) is the ability to store business logic as calculations directly in the cube. That way, you can ensure that a particular business metric is calculated the same way every time, no matter who is asking to view the metric and no matter what skill level they have. To store business logic in the cube, you use MDX, which is the abbreviation for multidimensional expression language. MDX is also a query language that presentation layer tools use to retrieve data from a cube.
I’ll begin this chapter by explaining how to add basic calculations to the cube. Users can access these calculations just like any other measure. The calculation designer includes several tools to help you create calculations, ranging from finding the right names of objects to selecting functions or templates for building calculations. In this chapter, you learn how to create simple calculations and how to use expressions to change the appearance of calculation results, so that users can tell at a glance whether a number is above or below a specified threshold. In addition, this chapter shows you how to use expressions to add custom members and named sets to a dimension. You will also learn about the more specialized types of expressions for key performance indicators to track progress towards goals and for actions to add interactivity to cube browsing.
To create a calculated member, open the Calculation tab of the Cube Designer in SSDT and click New Calculated Member in the toolbar, the fifth button from the left. The Form View asks for the information that we need based on the type of calculation we’re creating. For a calculated member, you need to supply a name and an expression, and set the Visible property at minimum, as shown in Figure 65. The name needs to be enclosed in braces if it has spaces in it or starts with a numeric value, and the expression must be a valid MDX expression.

In this example, a simple addition operation combines the results of evaluating two measures, Internet Sales Amount and Sales Amount. For each cell intersection of rows and columns in a query, Analysis Services evaluates each portion of the expression individually and adds the results. That is, Internet Sales Amount is evaluated in the context of the dimensions on the current row, the current column, and the current filter, as shown in Figure 66, and the same is true for Sales Amount. Then, Analysis Services adds the results to produce the value that is displayed for Total Sales Amount having the same combination of row, column, and filter members.

A calculation also has several properties, many of which are similar to measures:
Note: Presentation tools vary in their support of color and font expressions. Be sure to test a calculated member in each presentation tool that you intend to use and set user expectations accordingly.
Each calculated member becomes part of the cube’s MDX script. The MDX script is a set of commands and expressions that represent all of the calculations for the entire cube, including the named sets and key performance indicators that I explain later in this chapter. Using the MDX script, you can not only create calculations to coexist with the measures that represent aggregated values from the fact table, but you can also design some very complex behavior into the cube, such as changing values at particular cell intersections. However, working with the MDX script extensively requires a deep understanding of MDX and is beyond the scope of this book.
Tip: To learn more about MDX, see Stairway to MDX, an online tutorial. You can learn about working with MDX script at MDX Scripting Fundamentals at MSDN.
To help you build an MDX expression, the Calculation Designer includes several tools:
In the bottom left corner of the Calculation Designer is the Calculation Tools pane, as shown in Figure 67. The first tab of this pane displays Metadata, which contains the tree structure of the cube and its dimensions. At the top of the tree is the list of measures organized by measure group in addition to calculated members. Dimensions appear below the measures. In the metadata tree, you can access any type of object—the dimension itself, a hierarchy, a level of a hierarchy, or an individual dimension member.

To use the metadata tree, right-click the object to add to your expression, and click Copy. Next, right-click in the Expression box, and click Paste. You can type any mathematical operators that you need in the expression, and then continue selecting items from the metadata tree.
The Functions tab in the Calculation Tools pane contains several folders, which organize MDX functions by category. Figure 68 shows some of the navigation functions, which are very commonly used in MDX when you have hierarchies and want to do comparisons over time or percentage of total calculations.

It’s important to understand what object type a function returns so that you can use it properly in an expression. For example, the Children function returns a set of members. Let’s put this in context by looking at the Categories hierarchy, shown in Figure 69.

At the top of the hierarchy is the All member, which is the parent of the members that exist on the level below: Accessories, Bikes, Clothing, and Components. You can use the Parent function with any of these members to return that All member, like this:
|
[Product].[Categories].[Accessories].Parent |
Likewise, you can use the Children function to get the set of members below a specified member. When you use this function with the Bikes member, the result is a set containing the Mountain Bikes, Road Bikes, and Touring Bikes members:
|
[Product].[Categories].[Bikes].Children |
You can also use navigation functions to move forward and backward along the same level. For example, the NextMember function returns Road Bikes:
|
[Product].[Categories].[Mountain Bikes].NextMember |
Likewise, using the PrevMember function with Touring Bikes also returns Road Bikes. It’s more common to use NextMember and PrevMember functions with a date dimension, but the functions work with any type of dimension. To summarize, an expression for calculated members consists of references to measures or members by using a direct reference (such as [Product].[Categories].[Mountain Bikes]) or a function to get a relative reference (such as [Product].[Categories].[Accessories].Parent). You can also perform mathematical operations on these references.
The third tab in the Calculation Tools pane is Templates. This tab provides sample expressions that you can use for many common types of calculations, as shown in Figure 70. For example, in a sales cube, you might have a calculated measure for gross profit margin. To use the template, you must first switch to Script View by using the twelfth button on the toolbar. Position the pointer below existing expressions, right-click the template, and then click Add To Template.

Unlike selecting an object from the metadata tree, the template adds the complete script command to create a calculation as shown in Figure 71. The expression itself contains tokens that we need to replace with actual references from our cube. You can do that in Script View or you can toggle back to Form View (the eleventh button in the toolbar).

Either way, the completed expression requires the sales amount and cost measures. In addition, you must add parentheses to the numerator expression to get the right order of operations and change the format string. That is, you must subtract cost from sales to get the margin before performing the division operation, like this:
|
CREATE MEMBER CURRENTCUBE.[Measures].[Gross Profit Margin] AS ([Measures].[Sales Amount] - [Measures].[Total Product Cost]) / [Measures].[Sales Amount] , FORMAT_STRING = "Percent"; |
A common way to use navigation functions is to retrieve values for a different time period. For example, to retrieve sales for a previous period, you can combine the sales amount measure with the PrevMember function in conjunction with a date hierarchy like this:
|
([Measures].[Sales Amount], [Order Date].[Year].PrevMember) |
This type of expression is known as a tuple expression. It is a coordinate that tells Analysis Services where to find information in the cube. A tuple can contain members only, which is why it’s important to know what a function returns. Because PrevMember returns a member, it’s valid for the tuple expression in the previous example.
By using a navigation function in a tuple expression, you can reposition Analysis Services away from a current member to a different member by moving forward or backward in the same level or up or down within a hierarchy. For example, in Figure 72, the Previous Period Sales Amount (based on the tuple expression defined previously), you can see the value for 2006 matches the Sales Amount value for 2005, which is the previous member on the Year level of the Year user-defined hierarchy of the Order Date dimension. Similarly, the Previous Period Sales Amount value for Q2 2006 is the Sales Amount value for Q1 2006, the previous member on the Quarter level of the hierarchy. The same is true at the month level, where the May 2006 value for Previous Period Sales Amount matches the April 2006 value for Sales Amount.

Calculated members have color and font properties that are not available for measures. These color and font properties can be based on expressions, and are configured at the bottom of the Form View. However, if you know the correct syntax, you can add these expressions directly to the MDX script.
In Form View, expand the applicable section. For example, in the Color Expressions, you have the option to configure Fore Color to set the font color or Back Color to set the fill color of the cell containing a value. In the Font Expressions, you can set Font Name or Font Size. You can also set Font Flags when you want to control whether to display a value using bold or italic font styling.
Typically, you use conditional expressions for colors and fonts. A conditional expression uses the IIF function, which has three arguments. The first argument is a Boolean expression that must resolve as true or false. Usually you compare a value to a threshold here. The second argument is the value returned by the function when the Boolean expression is true, while the third argument is the value returned when the Boolean expression is false, like this:
|
iif([Measures].[Gross Profit Margin]<.1, 255 /*Red*/, 0 /*Black*/) |
Tip: You can use the color or font picker that is displayed to the right of the expression box to set the appropriate code in your expression. For example, you can select the color red in the picker and the value 255 appears in your expression along with a commented string, /*Red*/, as a reminder of the value’s meaning..
You should then test the result in the presentation tools that users will have, such as Microsoft Excel, as shown in Figure 73. Here, you can see the red font is displayed only wherever the gross profit margin is less than 10%.

Sometimes you need to create calculations that affect how measures get computed, but are not themselves measures. These are calculated members that are part of a dimension, and are also known as custom members. Let’s say that you want to support analysis of one product category relative to all other product categories. You can create a calculated member to include a total of all product categories except for Bikes. When you create a calculated member, as shown in Figure 74, you assign a name for the member that is unique within the parent hierarchy and define its parent member.

After that, you define the expression for the calculated member. In this example, the calculated member’s expression uses the Aggregate function, but any function for aggregation is permissible here. That is, you can use Sum, Avg, Min, Max, DistinctCount, or Count instead.
|
Aggregate( {[Product].[Category].[Accessories], [Product].[Category].[Clothing], ) |
By using the Aggregate function in the expression, you can ensure the correct values are computed for any measure that you use in a tuple expression with the calculated member. Some measures are additive, like sales and costs, while other measures are non-additive, like Gross Profit Margin. With non-additive measures, Analysis Services must sum the component parts of the expression first. With Gross Profit Margin, it needs to sum the numerator separately from the denominator, and then perform the division operation last. The Aggregate function ensures the correct order of operations, as shown in Figure 75.

The Aggregation function, like the other aggregation functions, requires a set as its first argument. A set is a collection of members from the same dimension and hierarchy. Notice that in the previous expression, the set is enclosed in braces and consists of specific members—Accessories, Clothing, and Components. In other words, the set definition explicitly lists every member of the Category hierarchy except Bikes and the All member.
Because the cube often has different types of measures, it is likely that you will need to use a conditional expression to define the format string. A CASE statement might be easier to read than a nested IIF function when there are multiple possible conditions, like this:
|
case |
You can switch to Script View to more easily work with a complex conditional expression. To do this, click the twelfth button on the Calculation Designer toolbar. Another property that you might need to set in Script View is Language. Sometimes Excel or other presentation layer tools are unable to properly interpret a format string, so the language property is used to give it a hint. The complete calculated member definition in Script View looks like this:
|
CREATE MEMBER CURRENTCUBE.[Product].[Category].[All].[Other Categories] AS Aggregate( {[Product].[Category].[Accessories], [Product].[Category].[Clothing], [Product].[Category].[Components]} ), FORMAT_STRING = case when [Measures].CurrentMember is [Measures].[Gross Profit Margin] then "Percent" when [Measures].CurrentMember is [Measures].[Order Quantity] or [Measures].CurrentMember is [Measures].[Internet Order Quantity] then "#,#" else "Currency" end , VISIBLE = 1, Language = 1033; |
Tip: The language identifier for United States English is 1033. If you need to use a different language identifier, refer to http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx to locate the correct value.
You can also use the Calculation Designer to develop named sets. A named set is a special type of calculation that returns a collection of dimension members, rather than a specific value like a calculated measure. You can create a named set by defining a list of members as described in the previous example, or you can use a set function to generate a set at query time. For example, you can use the TopCount function to produce a set of the three products that have the highest sales, like this:
|
TopCount([Product].[Product].[Product].Members, 3,[Measures].[Total Sales Amount]) |
The first argument of the TopCount function requires a set. In this case, the expression for the first argument uses the Members function to get all the members that exist on the Product level of the Product hierarchy of the Product dimension. The second argument specifies how many members to include in the final set. The third argument specifies the measure for which Analysis Services needs to perform a descending sort of the members. In other words, this function takes the set of products, finds the total sales amount for each of them, sorts the products in descending order, and then returns the first three members of the resulting set.
When you create a named set, you specify whether you want the contents of the named set to remain constant, or the contents to change based on the context of the current query. In the Additional Properties section of the form view for a named set, select Static for a constant set or Dynamic for a set that adjusts to the query context in the Type drop-down. Figure 76 shows the effect of changing the year filter on each type of named set.

On the left side of Figure 76, the pivot tables display a static named set while the pivot tables on the right side of the figure display a dynamic named set. The pivot tables on the top row are identical because no filters are applied to the static and dynamic pivot tables. They include the same top three products and the same values. However, the bottom row illustrates the difference between static and dynamic named sets when a filter, such as Q1 2008, is applied. On the left, the static named set includes the same three products that are displayed in the unfiltered pivot table above it, but the values in the Total Sales Amount column are applicable to Q1 2008 for those products. On the right, the dynamic named set displays a different set of three products. These three products are the top three selling products within the filtered time frame, Q1 2008, and the Total Sales Amount column displays the sales for those three products in that quarter. Thus, a static named set retains the same members regardless of filters in the query, whereas a dynamic named set includes different members depending on the query filters.
Another type of calculation that you can add to the cube is a key performance indicator (KPI). Technically, a KPI is not a single calculation, but a set of calculations that quantitatively describe how a value compares to a goal. In addition, a KPI includes one symbol to represent progress toward a goal and another symbol to represent the direction the KPI value is trending at a selected point in time.
Although KPIs are calculations, you use the KPI Designer to define each KPI rather than the Calculation Designer. Figure 77 shows the set of calculations for a Gross Profit Margin KPI:

After you click the New KPI button in the toolbar, you define a KPI with four separate expressions: value, goal, status, and trend. Let’s look at each of them separately.
Unlike the Calculation Designer, you can use a browser built into the KPI Designer to check the results of your KPI definition. You must first deploy your project to save the KPI definition, and then click the Browser View button (the tenth button on the KPI Designer toolbar). In the browser, you can define filters to observe the changes to the KPI results, as shown in Figure 78. To view status and trend values that depend on time comparisons, you must include a Date dimension in the filter.

Tip: After changing a dimension filter in the browser view, click anywhere in the Display Structure area to apply the filter and update the KPI values.
Another way to enhance a cube is to define an action on the Actions tab of the Cube Designer. An action is a feature of Analysis Services that allows the user to initiate a process in the context of a current query, such as opening a webpage or launching a report. You can add any of three types of actions to a cube: a standard action, a drillthrough action, or a reporting action.
Regardless of its type, an action has a target, which is the object in the cube that the user clicks to trigger the action. A target can be a member of an attribute, hierarchy, level, or dimension; a cell, hierarchy, or level; or even the cube itself. When a user explores the cube, the client application displays available actions associated with a target. The steps to display or launch an action depend on the client application. For example, Excel displays available actions when you right-click a target, such as Mountain Bikes, as shown in Figure 79. In this case, the action is assigned a target type of attribute members and a target object of Product.Subcategory is displayed in Excel. In the submenu, click Additional Actions to view the available actions, such as “Search for Mountain Bikes” in this example.

Note: Before adding actions to your cube, be sure the client application supports this feature
A standard action is an action that returns data or launches another application based on the selected target. When you define an action, you specify an MDX expression that resolves as a string that the action provides to another application for execution. You also assign one of the following action types to your action:
Note: Most of these action types require the use of a custom application to interpret and execute the action and to use the results of execution in some way. If you are using Excel or PerformancePoint Services, only the URL action is supported.
Because the URL action type is the most commonly used of the available action types, let’s consider an example in which the action must open a webpage that displays Bing search results for a selected product subcategory. To create the action, click New Action (the fifth button in the toolbar) and assign a name. In this case, the target Type is Attribute Members and the Target Object is Product.Subcategory. For this action, you create an Action Expression using an MDX expression that concatenates static text and the name of the selected member like this:
|
"http://www.bing.com/search?q=" + [Product].[Subcategory].CurrentMember.Name |
You can also configure other settings for an action by expanding the Additional Properties section for the action:
|
"Search for " + [Product].[Subcategory].CurrentMember.Name |
You add a drillthrough action to allow users to see the underlying detail data associated with a selected cell. To create the action, click New Drillthrough Action (the sixth button in the toolbar), assign a name, and select a specific measure group or all measure groups as the action target. In the Drillthrough Columns section, you select one or more combinations of dimensions and attributes to return in the drillthrough results, as shown in Figure 80.

You can configure additional properties like those available for a standard action, as well as the following properties specific to the drillthrough action:
In the client application, you invoke the drillthrough action from a cell containing a measure in the measure group specified as the action target. Figure 81 shows a portion of the data returned by a drillthrough action in Excel.

When a user launches a reporting action, the current context of the user’s selection can pass to a report hosted in Reporting Services as one or more parameters. The reporting action requires you to specify a target type and target object just like a standard action. You also specify the following report server properties:
If the report contains parameters, you can provide static text or dynamic MDX expressions to configure parameter values for report execution. You type the parameter name as it appears in the report definition language (RDL) file and provide an expression that resolves to a value that is valid for that parameter, as shown in Figure 82.

Writeback is one of the less commonly used features of Analysis Services for two reasons. First, it requires a client application that supports it. Second, writeback is used by a relatively small number of users who need to do planning, budgeting, or forecasting, as compared to the number of users performing analysis only. In fact, the most common use of writeback in Analysis Services is in financial analytics, whether working on budgets by department or forecasting revenues and expenses. Before users commit to a final budget or forecast, they might go through several iterations of planning and can use writeback to support that process.
Note: In this section, I review the concepts for cell writeback and dimension writeback. To learn more about writeback, see Building a Writeback Application with Analysis Services.
Data normally becomes part of a cube when events occur that we want to track, such as sales or general ledger entries. However, with writeback, users can update the cube with values that they anticipate might occur, and can use the aggregation capabilities of Analysis Services to review the impact of these updates. To facilitate this activity, a client application that supports writeback is required to update a cell. In other words, writeback activity requires an update to fact data. To add data to the cube to describe what you expect or would like to see happen, you can use writeback to update values in a measure group at the intersection of selected dimension members. Typically, data captured for the writeback process is at a high-level, such as a quarter, and the Analysis Services database must contain instructions for allocating to leaf-level members like days.
Analysis Services stores the writeback data in a separate ROLAP partition, regardless of the cube storage mode (as described in Chapter 4, “Developing cubes”). In fact, the writeback process updates a relational table and then performs an incremental process of the MOLAP partition to bring it up-to-date to match the source. Any subsequent queries to the cube return data from the MOLAP partition only. The cost of this incremental update is small compared to the impact of ROLAP queries.
To implement cell writeback, you create a writeback partition for a measure group, which can use MOLAP or ROLAP storage. As a user adds or changes values in the cube, the values stay in memory for the user’s current session and no one else can see those values. That way, a user can test the impact of changes made before finalizing the input. When finished, a COMMIT command in the writeback application recalculates user input as weighted values to leaf-level cells if the input was at higher levels. After that, T-SQL Insert statements are executed to add corresponding records to the writeback partition. If the partition uses MOLAP storage, Analysis Services performs an incremental update (as described in Chapter 6, “Managing Analysis Services databases”) to bring the cube up-to-date for faster performance.
Note: You can use cell writeback in any edition of Analysis Services.
You can implement dimension writeback when you want to give users the ability to add new members to the dimension without requiring them to add it to the source application. However, you must have a client tool that supports dimension writeback. You can also use security to control who can use this feature, as explained in Chapter 6.
Dimension writeback is helpful when a source application is not available to maintain the members, such as a Scenario dimension. As an example, you might have a dimension like this in the data warehouse for which you preload it with members but don’t have a regular extract, transform, and load process to continually add members over time. In the AdventureWorksDW sample database, the DimScenario table has only three members to start: Actual, Budget, and Forecast.
To implement dimension writeback, you need to modify the dimension design. Specifically, you change the WriteEnabled property from its default value of False to True, and then deploy the change. If users are working with an application that supports dimension writeback, they can then add new members as needed. If you have multiple versions of budgets or forecasts that you want to keep separate during the planning process, you can also use dimension writeback to create a name used to track each version. For example, in a Scenario dimension, each member is either associated with actual data or a specific version of a budget or forecast, such as Budget v1 or Forecast v2.3.
When a user adds a new member with dimension writeback, Analysis Services updates the relational source immediately. For this reason, you can use dimension writeback only with a dimension that has a single table or an updateable view as its source. An incremental update (as described in Chapter 6) then gets the data from the relational source to update the Analysis Services database, and any subsequent queries to get dimension metadata include the new member. There is no separate storage of dimension members added in this way.
Note: You can only use dimension writeback in the Enterprise or Business Intelligence editions of Analysis Services