Analysis Services (RSS)

Many-to-Many Dimensions: Query Performance Optimization Techniques

More than one year ago I published a paper about design patterns for many-to-many dimension relationships. Since then, I built other models and I got more experience about possible performance optimizations. Unfortunately, I still hadn't time to write about it and this topic requires verbose document and analysis to be reproducible and understandable by everyone. Luckily, now there is a white paper (Analysis Services Many-to-Many Dimensions: Query Performance Optimization Techniques) downloadable from Microsoft site that explore this world and offers an interesting set of scenarios and possible optimizations, with numbers that explain when, what and how you can expect to optimize a model based on one or more many-to-many dimension relationships.

I already read the document and it is pretty good. Unfortunately, in the real world there are still complex scenarios that cannot be optimized with the techniques used here. I hope that in future versions the SSAS engine will provide some more advanced optimizations for similar cases. One of the first step could be simply automating the optimizations made "by hand" like the Matrix Relationship Optimization shown in the paper. Another would be the simplification for defining efficient aggregations when many-to-many relationships are involved (now you could create a lot of aggregations that are unused when M2M are involved, and sometimes users query a cube only using M2M relationships - you have to tune the aggregations manually).




Cross-posted from SQLBlog! - http://www.sqlblog.com


DefaultMember, subcubes and non-aggregatable attributes

Today I discovered that DefaultMember might result in a member other thant the default dimension member.

Reading documentation, I got the idea that DefaultMember would be always the default member defined for a dimension into a cube. In reality, the default member could change into a subcube that excludes the original default member from a calculation. This is important because writing MDX Scripts you cannot anticipate any possible use of your cube... and you should carefully consider when DefaultMember is the real appropriate syntax on a case-by-case basis. You have to be particularly careful whenever you use DefaultMember on a non-aggregatable attribute.

Let's look an example.

In Adventure Works you can write the following query.

WITH MEMBER Measures.DiffActual 
     AS (Scenario.Scenario.CurrentMember, Measures.Amount) 
        - (Scenario.Scenario.DefaultMember, Measures.Amount)
SELECT 
{ [Scenario].[Scenario].[Forecast], 
  [Scenario].[Scenario].[Budget] } ON COLUMNS,
{ Measures.Amount, Measures.DiffActual } ON ROWS
FROM [Adventure Works]

 

This query provides this result:

image

 

I'm not interested in the real meaning of this query. This is only an excuse to see the DefaultMember in action against a non-aggregatable attribute. The DiffActual calculated member has a formula that use Scenario.Scenario.DefaultMember. The default member for Scenario.Scenario attribute is Actual. The DiffActual row shows the difference between the scenario in column and the Actual scenario.

Now, if we encapsulate the previous query into a subquery that apparently should return the same members, we can write this query.

WITH MEMBER Measures.DiffActual 
     AS (Scenario.Scenario.CurrentMember, Measures.Amount) 
        - (Scenario.Scenario.DefaultMember, Measures.Amount)
SELECT 
{ [Scenario].[Scenario].[Forecast], 
  [Scenario].[Scenario].[Budget] } ON COLUMNS,
{ Measures.Amount, Measures.DiffActual } ON ROWS
FROM 
  (SELECT 
    { [Scenario].[Scenario].[Forecast], 
      [Scenario].[Scenario].[Budget] } ON COLUMNS
FROM [Adventure Works])

 

Before executing this query, what is the expected result? First time I thought "it should be the same", but in reality it is different, like shown here:

image

What happened? Simply, the SSAS calculation engine needs a default member included in the "context" - when you use a subquery, you are defining a subset of the cube space that must have an existing default member for each attribute - if a default member is outside of the subcube, it is replaced by another member of the same attribute.

In the case we are exmining, the default member of the subcube becomes Forecast. This default member depends on the order of members of the same attribute used in the subcube. If we invert Forecast and Budget members only in the subcube, the default member becomes Forecast instead of Budget, changing the calculated member result (based on DefaultMember syntax).

WITH MEMBER Measures.DiffActual 
     AS (Scenario.Scenario.CurrentMember, Measures.Amount) 
        - (Scenario.Scenario.DefaultMember, Measures.Amount)
SELECT 
{ [Scenario].[Scenario].[Forecast], 
  [Scenario].[Scenario].[Budget] } ON COLUMNS,
{ Measures.Amount, Measures.DiffActual } ON ROWS
FROM 
  (SELECT 
    { [Scenario].[Scenario].[Budget], 
      [Scenario].[Scenario].[Forecast] } ON COLUMNS
FROM [Adventure Works])

 

This is the result for the query above:

image

To avoid this issue you can use a specific member instead of the keyword DefaultMember. In our example, the solution is:

WITH MEMBER Measures.DiffActual 
     AS (Scenario.Scenario.CurrentMember, Measures.Amount) 
        - (Scenario.Scenario.[Actual], Measures.Amount)
SELECT 
{ [Scenario].[Scenario].[Forecast], 
  [Scenario].[Scenario].[Budget] } ON COLUMNS,
{ Measures.Amount, Measures.DiffActual } ON ROWS
FROM [Adventure Works]

 

With this syntax, any use of this query as a subcube in another query will not have the side effects we have seen before.

Why this is so important? Simply because Excel 2007, often used as a client by end users, makes heavy use of subqueries in MDX generated by PivotTable. I had to substitute all DefaultMember in MDX Scripts with well-known member names (this also affects my DateTool dimension, which I will update soon with this and other improvements).

Final note: a special thanks to Mosha Pasumansky for illuminating me on hidden secrets of DefaultMember. Mosha also said that using the member name of default member (instead of DefaultMember keyword) improves query performance.




Cross-posted from SQLBlog! - http://www.sqlblog.com


Red color for negative numbers in Analysis Services 2005

Sometimes there is a simple way to solve an issue. For example, if you want to color all measures (including calculated measures) in red when the value is negative, you can simply write:

SCOPE( Measures.AllMembers );
    FORE_COLOR(THIS) = IIF( Measures.CurrentMember < 0, 255, 0);  // 255 = RED
END SCOPE;

 

MDX Script is so better than classical properties for each measure in these cases...




Cross-posted from SQLBlog! - http://www.sqlblog.com


Aggregation Usage issues with a measure group not linked to a dimension

I just posted this bug on the Microsoft Connect site. Please vote it if you think it would be a useful change. This is the issue description.

If you set the"Aggregation Usage" to true for an attribute of a dimension (i.e. Customer), you cannot run the aggregation design wizard on a partition of a measure group that is not linked to that measure group.
You receive an error message, but it would be preferable a Yes/No dialog box that allow the user to continue considering an "Aggregation Usage" set to "Default" for that dimension only for this measure group.
Actually you can workaround this issue in two ways:

  1. Changing the Aggregation Usage before/after using the aggregation design wizard (setting the desired value for the measure group you want to optimize)
  2. Manually designing the aggregation (not a very comfortable practice)

I understand it is not a real bug, but the message is not coherent with what you can do anyway by changing aggregation usage setting.

I'm working on optimizing aggregations in these days and this limitation has been very annoying today, requiring me to write a set of specification just to explain how to redesign aggregations if in the future someone will make a structural change to the cube...




Cross-posted from SQLBlog! - http://www.sqlblog.com


Strange bug in SSAS 2005

I have been notified of this strange bug in Analysis Services 2005 that is easily reproducable.

******************************************************************
PLEASE DO NOT TRY THIS TEST ON A PRODUCTION SERVER! IT HANGS ANALYSIS SERVICES!!!
******************************************************************

  • Open SQL Server Management Studio
  • Browse the Adventure Works cube
  • Put Promotions/Promotions hierarchy on the filter area (no measures in the data area)
  • Expand Promotions and uncheck "Customer"
  • Drag Promotions hierarchy from filter to rows
  • Drill the "Reseller" member - at this point you may observe a long wait but the worst is that msmdsrv.exe process on the server (it's the Analysis Services service) is consuming all available virtual memory - you need to restart Analysis Services service at this point

I tested it on a SP2 server.

First of all, since it is a "Denial of Service" for Analysis Service, it is best to remove the Adventure Works sample from a production server - but you never put sample databases on production server, right? :-)

I hadn't time to test it more to understand what is the real issue here to understand what is the impact on other databases. The strange thing is that if you do not move the Promotions hierarchy on the filter area before, the problem do not arises.




Cross-posted from SQLBlog! - http://www.sqlblog.com