In the previous part, we looked at joining two cubes with shared dimensions together so that we could review data from our main cube and data from a related cube side-by-side to get additional insights without the need for drilling, additional views on the same canvas, synchronization, creating a book in PAfE etc.
In this part, we will take the stitching, or joining of cubes one step further by joining two related cubes where the rows dimensions is similar but at a different level of granularity to the main cube’s dimension.
The example we will look at is one that no doubt is familiar and solves many of the lookups and the to-and-fro when working with allocation models. Instead of having separate views for the initial high-level values, the ratios needed to perform the allocation and the end result, we can combine these into one view.
I have a Brand Planning model where planners have planned the sales by Brand for the year.
Products can be rolled up into Brands with Products being the lower, leaf level item. The view for sales planning needs to be at the lowest level i.e. Product and Customer.
To allocate or break back the Brand Planning to Product and Customer, we will use some historic sales ratios based on the actual sales of each Product and Customer in relation to the Brand and Total Customers.
To ensure that we balance and fully allocate our planned Brand sales, we will need to ensure that the following is complete and accurate:
· Brand Planning is complete i.e. planned volumes captured by Brand and Period are available
· Historic sales ratios have been derived or input for all Brands:
o Derive ratios based on past Sales
o Manually capture expected behaviour for new Brands with no history
· Perform allocation or breakback from Brand to Product
· Review results and confirm allocations
· Correct any missing historic sales ratios and rerun where required
For those who have done these kinds of models, you will appreciate all the views necessary to try and allow users to get a view at each stage as well as work through the process to configure and validate at each step.
Let's see how we can deliver a better solution by joining related data together.
Cube and Dimension Comparison
Let’s look first at the two cubes we will be working with to understand the dimensionality of each:
Sales
|
|
Brand Planning
|
Scenario
|
|
Scenario
|
Period
|
|
Period
|
Customer
|
|
<not applicable>
|
Product (leaf)→
|
related to →
|
Brand (parent)
|
Sales Measures
|
|
Brand Planning Measures
|
From the table above, we can see that the Sales and Brand Planning cubes are similar and share some dimensions but not all. Product and Brand are similar but the granularity is different.
In the Brand dimension we have a simple aggregation of Brands into a total.

In the Product dimension we have leaf level SKUs which roll up into various aggregations used for analysis e.g. Total Products, Total Brands, Total Demographics and Total Statistical Groups.
The Total Brands rollup is an aggregation of leaf level SKUs into a Brand based on the mapped attribute and then each Brand is aggregated to Total Brands.

For our review, we want to compare the Brand totals between the cubes. Although Brand IDs like “14” exist in both dimensions, they are not technically the same member since they reside in separate dimensions. To align them, we need to map members from the Product dimension to their corresponding Brand dimension members, leveraging the shared Brand ID.
In previous examples we just referenced the CurrentMember function to read across cubes where the dimensions were shared and the member in the same dimension but just a different cube.
In this case we will need to convert members in the Product dimension to an equivalent member in the Brand dimension. This should not be too complicated as the Brand ID is consistent between the two dimensions. The member “14” in Product has the same meaning in Brand.
Historic Sales Ratios
After we have processed Actuals to derive historic ratios, we have the following view of a month, the sales units and the respective ratio for each Product to its parent Brand:

It is these ratios that we will need when performing our allocation from the Brand down to each Product and Customer. Ignore that Total Brands value of 2000% as this is simply the aggregation across all the SKUs and not used.
You will notice that for some Brands like 14-Mediproxen and 60-Healuron, no sales were made and thus our Historic Sales Ratio is 0%. Should we plan against these Brands, we will not have any basis on which to allocate the Brand units down to Products and Customer.
To handle this, the model includes an override mechanism where users can manually input expected sales ratios per Product and Customer. This override enables allocation to proceed even in the absence of historical data. The final ratio used in the allocation is either the historic ratio or, where available, the override value.
This is an essential part of any allocation model as there are always new members with no history and they need to be updated whether manually or via a surrogate for the model to balance.
Brand Planning
Our Brand Planning is where the high-level sales are planned. The Planners have completed their initial planning and we can see the following expectation for the planning periods:

These are the numbers we want to see in our Sales cube by Product and Customer, after we have performed the allocation. We would want to view the planned Brand sales side-by-side with the Sales cube units, and include a variance column to make it easy to spot differences.
Legacy Sales Review Approach
Our initial Sales cube view is as follows:

After we run our process to do the allocation or breakback, we have the following in the Sales cube:

But is this correct? Does it reconcile to the Brand Planning cube volumes?
If we look at the Brand Planning cube view we see the following:

To see where we align and where we differ I typically need to have the two views above open and then go line by line or Brand by Brand to see where the differences are.
This is inefficient and can waste a lot of time switching back and forth and then having to go to another screen to check the Historic Sales Ratios too.
And this right here is the crux of the problem we are trying to solve in this part of the blog series – disparate views, switching back and forth and an overall inefficient approach.
MDX Sales Review Approach
In this approach we want to bring in volumes from the underlying cubes and present them in a single view so that a user can easily work through them to potentially see what needs to be allocated, how the volumes will be allocated, the allocated volumes and any variances after allocation.
The Base View
We will want to switch our previous view so that we can place the Brand Planning Units on the columns along with the Historic Sales Ratio, Sales Units (post allocation) and Variance (Brand cube to Sales cube). Period can be placed in the context or titles as slicers to allow us to view a single period or the year as a total.
Our base view on the Sales cube can be configured per below showing the volumes allocated from the Brand Planning cube by Product and then rolled up to its Brand:

Adding Historic Sales Ratios
Next we can add the Historic Sales Ratios as columns as they are also stored in the Sales cube. They are however stored against Periods in the Prior Year and against Actuals, not Rolling Forecast.
The references for all three Historic Sales Ratios are identical apart from the measure member. We thus need something like this:
[Sales].(
[Scenario].[Actual],
[Period].[<prior year period>],
[Customer].CurrentMember,
[Product].CurrentMember,
[Sales Measures].[Historic Sales Ratio])
To dynamically reference the corresponding prior year Period for our selected context, we use the StrToMember function and the Prior Year attribute defined on our Period dimension. Our MDX lookup leveraging the StrToMember function is as follows:
[Sales].(
[Scenario].[Actual],
StrToMember("[Period].[Period].[" +
[Period].CurrentMember.Properties("Prior Year", TYPED) + "]"),
[Customer].CurrentMember,
[Product].CurrentMember,
[Sales Measures].[Historic Sales Ratio])
The above MDX should return the Historic Sales Ratio from last year for the specific Product and Customer we are looking at. If we were looking at 2024-JAN for example, our attribute returned should be 2023-JAN and then this is converted to a member and the Historic Sales Ratio read for this Period.
We use this approach to define three calculated members in the query: Sales Ratio, Sales Ratio-Override, and Sales Ratio-Final.
In a previous part of this series we used StrToMember when dealing with the User Preferences cube. We needed to take the text value of our UserName and convert it to a member in the }Clients dimension.
STRTOMEMBER("[}Clients].[" + UserName + "]")
In a similar manner in the above Sales lookup MDX, I am getting the value of the Period's Prior Year attribute e.g. 2023-JAN. As this is just a string or text value it cannot be used by itself in the MDX to reference a member and we need to convert it to a valid member in the Period dimension first. We can use StrToMember again using the following MDX:
StrToMember("[Period].[Period].[" +
[Period].CurrenMember.Properties("Prior Year", TYPED) + "]")
From the above snippet, you should be able to see the components quite clearly. I am using the TYPED modifier as standard in all my MDX code now to ensure that I return the results as the correct data type i.e. string or numeric.
Adding the above to our base view and replicating the MDX to show the three Sales Ratio columns we need, our MDX could be written as follows:
WITH MEMBER [Sales Measures].[Sales Measures].[Sales Ratio] AS
[Sales].([Scenario].[Actual],
StrToMember("[Period].[Period].[" +
[Period].CurrentMember.Properties("Prior Year", TYPED) + "]"),
[Customer].CurrentMember,
[Product].CurrentMember,
[Sales Measures].[Historic Sales Ratio]), SOLVE_ORDER=1, FORMAT_STRING='0.00%'
MEMBER [Sales Measures].[Sales Measures].[Sales Ratio-Override] AS
[Sales].([Scenario].[Actual],
StrToMember("[Period].[Period].[" +
[Period].CurrentMember.Properties("Prior Year", TYPED) + "]"),
[Customer].CurrentMember,
[Product].CurrentMember,
[Sales Measures].[Historic Sales Ratio-Override]), SOLVE_ORDER=2, FORMAT_STRING='0.00%'
MEMBER [Sales Measures].[Sales Measures].[Sales Ratio-Final] AS
[Sales].([Scenario].[Actual],
StrToMember("[Period].[Period].[" +
[Period].CurrentMember.Properties("Prior Year", TYPED) + "]"),
[Customer].CurrentMember,
[Product].CurrentMember,
[Sales Measures].[Historic Sales Ratio-Final]), SOLVE_ORDER=3, FORMAT_STRING='0.00%'
SELECT NON EMPTY
{
[Sales Measures].[Sales Measures].[Sales Ratio],
[Sales Measures].[Sales Measures].[Sales Ratio-Override],
[Sales Measures].[Sales Measures].[Sales Ratio-Final],
[Sales Measures].[Sales Measures].[Units]
} ON 0,
{
Descendants(TM1SubsetToSet([Product].[Product],"Total Brands","public"))
} ON 1
FROM
[Sales]
WHERE (
[Scenario].[Scenario].[Rolling Forecast],
[Period].[Period].[2024^2024-JAN],
[Customer].[Customer].[Total Customers])
That will give us the first part of the view we need to reconcile our allocation numbers:

The units in the above view is still based on the legacy approach and contains values at a Product level rolling up to each Brand and to Total Brands.
Although we now see the ratios and allocated units, this view does not yet show:
- Where Brand Planning has volumes but no allocation occurred, due to missing ratios, and
- The total variance between the planned and allocated volumes per Brand.
Identifying and resolving these gaps is essential for planning integrity. In the next section, we’ll bring in the Brand Planning figures and calculate a variance column directly in the view.
Adding Brand Planning Volumes
In this step we want to add two additional columns to complete our view:
- Brand Planning Units and
- Variance, between the Brand Planning cube and Sales cube.
Reading from the Brand Planning cube will require us to transform the Brand in the Product dimension to a corresponding member in the Brand dimension.
Our base code for the lookup to read the units from the Brand Planning cube is as follows:
[Brand Planning].(
[Scenario].CurrentMember,
[Period].CurrentMember,
[Brand].[<based on product member>],
[Brand Planning Measures].[Units])
In a similar manner to how we derived the Prior Year period, we will need to derive the Brand using the member from our Product dimension.
[Brand Planning].(
[Scenario].CurrentMember,
[Period].CurrentMember,
StrToMember("[Brand].[Brand].[" +
[Product].CurrentMember.Name + "]"),
[Brand Planning Measures].[Units])
We look at the current member in the Product dimension and take its name. Using the name we then convert this to a member in the Brand dimension using the StrToMember function.
We can now add the above piece of MDX code to do the lookup into our view:
WITH
MEMBER [Sales Measures].[Sales Measures].[Sales Ratio] AS [Sales].(
[Scenario].[Actual],
STRTOMEMBER("[Period].[Period].[" + [Period].CURRENTMEMBER.PROPERTIES("Prior Year" , TYPED) + "]"),
[Customer].CURRENTMEMBER,
[Product].CURRENTMEMBER,
[Historic Sales Ratio]), SOLVE_ORDER = 1, FORMAT_STRING = '0.00%'
MEMBER [Sales Measures].[Sales Measures].[Sales Ratio-Override] AS [Sales].(
[Scenario].[Actual],
STRTOMEMBER("[Period].[Period].[" + [Period].CURRENTMEMBER.PROPERTIES("Prior Year" , TYPED) + "]"),
[Customer].CURRENTMEMBER,
[Product].CURRENTMEMBER,
[Historic Sales Ratio-Override]), SOLVE_ORDER = 2, FORMAT_STRING = '0.00%'
MEMBER [Sales Measures].[Sales Measures].[Sales Ratio-Final] AS [Sales].(
[Scenario].[Actual],
STRTOMEMBER("[Period].[Period].[" + [Period].CURRENTMEMBER.PROPERTIES("Prior Year" , TYPED) + "]"),
[Customer].CURRENTMEMBER,
[Product].CURRENTMEMBER,
[Historic Sales Ratio-Final]), SOLVE_ORDER = 3, FORMAT_STRING = '0.00%'
MEMBER [Sales Measures].[Sales Measures].[Brand Planning Units] AS [Brand Planning].(
[Scenario].CURRENTMEMBER,
[Period].CURRENTMEMBER,
STRTOMEMBER("[Brand].[Brand].[" + [Product].CURRENTMEMBER.NAME + "]"),
[Sales Measures].[Sales Measures].[Units]), SOLVE_ORDER = 4, FORMAT_STRING = '#,##0;(#,##0)'
SELECT
{
[Sales Measures].[Sales Measures].[Sales Ratio],
[Sales Measures].[Sales Measures].[Sales Ratio-Override],
[Sales Measures].[Sales Measures].[Sales Ratio-Final],
[Sales Measures].[Sales Measures].[Brand Planning Units],
[Sales Measures].[Sales Measures].[Units]
} ON 0,
{
DESCENDANTS(
TM1SubsetToSet([Product].[Product] , "Total Brands" , "public"))
} ON 1
FROM
[Sales]
WHERE (
[Scenario].[Scenario].[Rolling Forecast],
[Period].[Period].[2024^2024-JAN],
[Customer].[Customer].[Total Customers])
The above code renders as follows:

The view is not ideal as we have errors on leaf-level Product lines as these do not exist as members in the Brand dimension. We cannot use a DIMIX or ElementExists in MDX to first test if a member is valid as these are not supported.
We can however make some assumptions that our master data is always aligned and Brands in the Product dimension should exist in the Brand dimension and leaf-level Products will never exist as Brands.
With these assumptions in mind we can change our MDX slightly to ignore the lookup to the Brand Planning cube where we are looking at Products:
WITH
MEMBER [Sales Measures].[Sales Measures].[Sales Ratio] AS [Sales].(
[Scenario].[Actual],
STRTOMEMBER("[Period].[Period].[" + [Period].CURRENTMEMBER.PROPERTIES("Prior Year" , TYPED) + "]"),
[Customer].CURRENTMEMBER,
[Product].CURRENTMEMBER,
[Historic Sales Ratio]), SOLVE_ORDER = 1, FORMAT_STRING = '0.00%'
MEMBER [Sales Measures].[Sales Measures].[Sales Ratio-Override] AS [Sales].(
[Scenario].[Actual],
STRTOMEMBER("[Period].[Period].[" + [Period].CURRENTMEMBER.PROPERTIES("Prior Year" , TYPED) + "]"),
[Customer].CURRENTMEMBER,
[Product].CURRENTMEMBER,
[Historic Sales Ratio-Override]), SOLVE_ORDER = 2, FORMAT_STRING = '0.00%'
MEMBER [Sales Measures].[Sales Measures].[Sales Ratio-Final] AS [Sales].(
[Scenario].[Actual],
STRTOMEMBER("[Period].[Period].[" + [Period].CURRENTMEMBER.PROPERTIES("Prior Year" , TYPED) + "]"),
[Customer].CURRENTMEMBER,
[Product].CURRENTMEMBER,
[Historic Sales Ratio-Final]), SOLVE_ORDER = 3, FORMAT_STRING = '0.00%'
MEMBER [Sales Measures].[Sales Measures].[Brand Planning Units] AS
IIF(ISLEAF([Product].CURRENTMEMBER),
0,
[Brand Planning].(
[Scenario].CURRENTMEMBER,
[Period].CURRENTMEMBER,
STRTOMEMBER("[Brand].[Brand].[" + [Product].CURRENTMEMBER.NAME + "]"),
[Sales Measures].[Sales Measures].[Units])
), SOLVE_ORDER = 4, FORMAT_STRING = '#,##0;(#,##0)'
SELECT
{
[Sales Measures].[Sales Measures].[Sales Ratio],
[Sales Measures].[Sales Measures].[Sales Ratio-Override],
[Sales Measures].[Sales Measures].[Sales Ratio-Final],
[Sales Measures].[Sales Measures].[Brand Planning Units],
[Sales Measures].[Sales Measures].[Units]
} ON 0,
{
DESCENDANTS(
TM1SubsetToSet([Product].[Product] , "Total Brands" , "public"))
} ON 1
FROM
[Sales]
WHERE (
[Scenario].[Scenario].[Rolling Forecast],
[Period].[Period].[2024^2024-JAN],
[Customer].[Customer].[Total Customers])
The resulting view looks cleaner and we are already getting a better picture of where the allocation has worked and where it has not:

Let's clean this up a bit more by only showing Total Brands and Brands without any leaf-level Products. Let's also add the variance column now that we have the Brand Planning Units and the Units in the Sales cube.
My updated MDX code is as follows:
WITH
MEMBER [Sales Measures].[Sales Measures].[Sales Ratio] AS [Sales].(
[Scenario].[Actual],
STRTOMEMBER("[Period].[Period].[" + [Period].CURRENTMEMBER.PROPERTIES("Prior Year" , TYPED) + "]"),
[Customer].CURRENTMEMBER,
[Product].CURRENTMEMBER,
[Historic Sales Ratio]), SOLVE_ORDER = 1, FORMAT_STRING = '0.00%'
MEMBER [Sales Measures].[Sales Measures].[Sales Ratio-Override] AS [Sales].(
[Scenario].[Actual],
STRTOMEMBER("[Period].[Period].[" + [Period].CURRENTMEMBER.PROPERTIES("Prior Year" , TYPED) + "]"),
[Customer].CURRENTMEMBER,
[Product].CURRENTMEMBER,
[Historic Sales Ratio-Override]), SOLVE_ORDER = 2, FORMAT_STRING = '0.00%'
MEMBER [Sales Measures].[Sales Measures].[Sales Ratio-Final] AS [Sales].(
[Scenario].[Actual],
STRTOMEMBER("[Period].[Period].[" + [Period].CURRENTMEMBER.PROPERTIES("Prior Year" , TYPED) + "]"),
[Customer].CURRENTMEMBER,
[Product].CURRENTMEMBER,
[Historic Sales Ratio-Final]), SOLVE_ORDER = 3, FORMAT_STRING = '0.00%'
MEMBER [Sales Measures].[Sales Measures].[Brand Planning Units] AS IIF(ISLEAF([Product].CURRENTMEMBER) , 0 , [Brand Planning].(
[Scenario].CURRENTMEMBER,
[Period].CURRENTMEMBER,
STRTOMEMBER("[Brand].[Brand].[" + [Product].CURRENTMEMBER.NAME + "]"),
[Sales Measures].[Sales Measures].[Units])), SOLVE_ORDER = 4, FORMAT_STRING = '#,##0;(#,##0)'
MEMBER [Sales Measures].[Sales Measures].[Variance] AS
[Sales Measures].[Sales Measures].[Units]-
[Sales Measures].[Sales Measures].[Brand Planning Units], SOLVE_ORDER = 5, FORMAT_STRING = '#,##0;(#,##0)'
SELECT
{
[Sales Measures].[Sales Measures].[Sales Ratio],
[Sales Measures].[Sales Measures].[Sales Ratio-Override],
[Sales Measures].[Sales Measures].[Sales Ratio-Final],
[Sales Measures].[Sales Measures].[Brand Planning Units],
[Sales Measures].[Sales Measures].[Units],
[Sales Measures].[Sales Measures].[Variance]
} ON 0, NON EMPTY
{
DESCENDANTS([Product].[Product].[Total Brands], 1, SELF_AND_BEFORE)
} ON 1
FROM
[Sales]
WHERE (
[Scenario].[Scenario].[Rolling Forecast],
[Period].[Period].[2024^2024-JAN],
[Customer].[Customer].[Total Customers])

This final unified view now provides a complete audit of the allocation process making it quite easy to see not only where there are problems but what the underlying issue is likely to be. It gives us the following insights:
- See historic sales ratios, overrides, and allocation logic in one place
- Compare planned Brand volumes vs. allocated volumes
- Quickly spot Brands with under- or over-allocated results
- Eliminate the need to switch across multiple views or screens
Capturing Missing Historic Sales Ratios
For the Brands viewable in the screenshot, we can see that the Sales Ratio-Final is zero and we would need to capture override values based on expected behaviour in the planning period. After this has been done we can run the allocation again and check our variances.
We could add conditional formatting to quickly identify which Brands have been planned for (units not zero) but do not have Historic Sales Ratios. To accomplish this we would need to create an additional calculated member. The logic would be something like: If we have planned units on the Brand, from our Brand Planning cube, but the Sales Ratio-Final is zero for that Brand, we are not going to be able to allocate the volume. We need to flag the cell with an indicator or colour by adding a conditional format rule.
I'll leave the conditional formatting as an exercise for the reader.
After discussion with the Brand Planning team, they think that we could expect the following split across the currently unallocated Brands:

Allocation Review
With the override values in for all Brands that were missing ratios, and after rerunning the allocation process, we now have an update view as follows:

With all the missing ratios input and the allocations run, we can now clearly see that across all Brands there is a variance of zero and volumes planning in the Brand Planning cube agree to the volumes allocated in the Sales cube.
Be aware that with these kinds of models using history, there may be negative ratios and it could be that at a total level you see a zero variance but two or more items may net each other off to zero giving you a false result. A positive variance on one Brand (+1,000 units) and a negative variance on another (-1,000 units) would net to zero at Total Brands.
Changes, Updates and Errors
What if we had done the allocation, checked that everything balanced but then the Brand Planners made additional updates and changed the Brand Planning values?
Let's assume that a mistake was made on 14-Mediproxen and the value was supposed to be 100,000 instead of 1,000. The Brand Planners also realised on review that they missed planning units for 62-Panacease and added these.
Refreshing our view would show these updates quite clearly as the volumes in the Sales cube are now out of date:

Where we had a zero variance previously and all balanced, we can see that two Brands are out of balance and we need to consider running the allocation again.
In this case, our ratios are fine as we have a Sales Ratio-Final for 62-Panaseaces and can simply perform the allocation.
Where we do not have an Historic Sales Ratio this would be highlighted and a ratio captured. The allocation would need to be rerun to update the volumes based on the new Brand Planning volumes and the Historic Sales Ratios.

With conditional formatting icons or shading you could make potential issues more visible. The following example may be all that is needed to show where we have issues either on the ratios or where the allocation needs to be rerun, or both.

Summary
In this article, we tackled a fairly common and often frustrating challenge when looking at allocation in Planning Analytics: reconciling high-level planning data with detailed, multi-dimensional allocations.
We walked through a practical scenario where Brand-level plans needed to be allocated down to Product and Customer levels using historical sales ratios. Along the way, we encountered and solved some realistic problems, such as:
- How to stitch data across cubes with related but non-identical dimensions,
- Handling mismatches or zero allocations caused by missing historical data,
- Using StrToMember and attributes for dynamic MDX references,
- Detecting allocation gaps and reconciling granular Product volumes to Brand volumes,
- Adding logic to identify when reallocation is necessary due to late changes.
By leveraging MDX to dynamically join cubes, lookup corresponding members, and computing variance on the fly, we created a single, interactive view that unifies planning input, allocation logic, and validation.
This eliminates the need for multiple screens, switching back and forth, manual cross-checks, or disconnected views—and gives planners and analysts the ease and clarity they need to confirm the allocation or identify issues.
Additionally the idea of incorporating conditional formatting and flags to highlight issues automatically ensures data quality and accountability without additional effort.
Ultimately, this approach empowers your allocation process to be:
- Faster – by reducing switching between cubes or views,
- More transparent – with everything visible in one place,
- More accurate – by making gaps and misalignments immediately obvious, and
- More scalable – supporting iterative updates and user overrides without losing integrity.
If you're working in Planning Analytics Workspace and want to move beyond static views or disconnected validation steps, the methodology discussed in this article offers an efficient, flexible and repeatable way forward.
This is the last part in this Introduction to MDX views in Planning Analytics series and I trust that you have learnt enough to take away the knowledge and build better models for yourself or your clients.
There will however be more articles on MDX but these will likely tackle a single topic and not form a series.
As always, please let me have your comments, questions and ideas and thanks for reading and following!
A summary of the MDX view related keywords used up to now:
Keyword
|
Description
|
Select
|
Specifies that you are looking to retrieve data from the cube
|
Non Empty
|
Removes all empty tuples from the specified set combinations on each axis. NON EMPTY can be used with both SELECT and ON clauses to filter axes or sets, not just "set combinations."
|
On
|
Allows you to specify the relevant axis
|
From
|
Specifies the underlying cube to retrieve the data from
|
Where
|
Filters the query or view based on the specified tuple. This is an oversimplification and may require additional reading as it behaves more like a global filter.
|
With
|
Tells the MDX to define a temporary calculated member or measure which lasts for the duration of the MDX query. It can also be used to define sets. WITH, MEMBER and AS are typically found together when working with calculated members.
|
Member
|
This defines the context and name of the calculated member as part of the WITH clause. Multiple Member statements can be added to the MDX to create additional calculated members.
|
As
|
Just a declarative almost like saying Let var = x and tells MDX to assign the calculation to the member
|
Solve_Order
|
Where queries are complex and there are dependencies between calculated members, you will need to consider using the solve order to prevent sequencing or logical errors, ensuring correct evaluation.
|
Format_String
|
Allows the calculated member to be formatted for presentation to the user. These could be numerical formats similar to what you have used in Excel, currency, dates, and conditional formatting to show formats for positive, negative and zero values.
|
Sum
|
Calculates the total of a numeric expression evaluated over a set.
|
Aggregate
|
Aggregates a set of tuples by applying the appropriate aggregation function, like sum or count, based on the context.
|
Min
|
Finds the minimum value of a numeric expression evaluated over a set.
|
Max
|
Finds the maximum value of a numeric expression evaluated over a set.
|
Stdev
|
Computes the standard deviation of a numeric expression over a set, measuring the amount of variation or dispersion of the set.
|
Var
|
Calculates the variance of a numeric expression evaluated over a set, indicating how spread out the numbers are.
|
IIF()
|
The Immediate IF statement allows us to evaluate an expression for a certain condition and the return a result if true and a different result if false.
|
Case, When, Else, End
|
The Case statement would be used where you expect multiple outcomes or need to extend the conditions beyond true and false.
The first WHEN statement that returns true is returned as the result.
An ELSE can be used for a catch-all scenario or default result.
|
.CurrentMember
|
Allows us to address the current member the view is dealing with from the context area or another axis.
|
.Properties("<propertyname>"<, TYPED>)
|
Allows us to retrieve a property value which may be an intrinsic like the Element_Level or custom like an attribute that a modeller added. Using TYPED ensures that a data type relating to the underlying definition is returned.
|
Instr()
|
Returns the position of a substring within a string, similar to TI's Scan()
|
Len()
|
Returns the length of a string
|
Left()
|
Returns the n number of characters from the start of a string
|
Right()
|
Returns the n number of characters from the end of a string
|
LCase()
|
Returns the lower case of a string
|
UCase()
|
Return the upper case of a string
|
ParallelPeriod()
|
Returns a member from a prior period in the same relative position within a parallel period e.g. same month, last year.
|
LastPeriods()
|
Returns a set of members starting from the specified member and going backwards through the hierarchy by a specified number of periods. If given a negative number, it moves forward through the hierarchy.
|
PeriodsToDate()
|
Returns a set of periods from the beginning of a specified level to the specified member. Typically used with year-to-date views as YTD() is not supported by TM1.
|
OpeningPeriod()
|
Returns the first member of a specified level within a specified period or its ancestor e.g. January as the first month in our year.
|
ClosingPeriod()
|
Returns the last member of a specified level within a specified period or its ancestor e.g. December as the last month in our year.
|
Lag()
|
Returns the member that is a specified number of periods before the specified member in the same level. The lag can be positive to move backwards or negative to move forwards.
|
Lead()
|
Returns the member that is a specified number of periods after the current member in the same level. Similar to Lag(), you can switch signage to move backwards but better to use Lag() and Lead() as designed for better readability.
|
UserName
|
Returns a string containing the client ID of the logged in user
|
StrToMember()
|
Converts a text string to a member in the specified dimension
|
TM1FilterByPattern()
|
Allows you to implement a wildcard search against a set of members. Don’t forget the third parameter that can specify an alias or attribute t be used to search against.
|
LookupCube
|
This function is an alternative to using the [cube].(tuple) syntax. A single line should contain the following syntax: LookupCube("cube","([dim1].[hier1].[member],…, [dimn].[hiern].[member])")
|
Further Reading
Learning MDX view in Planning Analytics-Part 1
Part 2 - Using Calculated Members in MDX
Part 3 - Aggregate Calculated Members in MDX
Part 4 - Using Calculated Members to Add Information
Part 5 - Working with Attributes in MDX Views
Part 6 - Working with Time Series in MDX Views
Part 7 - Using Lookup Cubes to Extend Your Queries
Part 8 - Joining Related Cube Data
TM1/Planning Analytics - MDX Reference Guide
Working with Time Related MDX Functions
Discovering MDX Intrinsic Members
Thanks to Wim Gielis for his insights and proof-reading this article.
#IBMChampions #PlanningAnalyticswithWatson#PlanningAnalytics #MDX #IBMChampion