Optimising MDX Calculations With The Unorder() Function
Reposted from Chris Webb's blog with the author's permission.
The Unorder() function is probably one of the least used functions in the whole of MDX. It exists only as a query performance hint and, since I had never up to now found a scenario where it did improve the performance of a calculation I had pretty much forgotten about it (as Books Online says, the optimisation it performs is applied automatically in many cases). However I was playing around with some calculations last week and found out that it does have its uses.
What does the Unorder() function do? It's a function that takes a set and returns a set, and what it does is remove any implicit ordering from that set. By default all sets in MDX are ordered, but for some types of operation that ordering is unimportant and ignoring it can result in faster query performance.
Take, for example, the following query on the Adventure Works cube which shows the number of customers who have bought something up to the current date:
WITH
MEMBER MEASURES.CUSTOMERSTODATE AS
COUNT(
NONEMPTY(
[Customer].[Customer].[Customer].MEMBERS
, {[Measures].[Internet Sales Amount]}
*
{NULL : [Date].[Calendar].CURRENTMEMBER})
)
SELECT
{MEASURES.CUSTOMERSTODATE}
ON COLUMNS,
[Date].[Calendar].[Date].MEMBERS
ON ROWS
FROM
[Adventure Works]
On my laptop it executes in 35 seconds on a cold cache. We can optimise the calculation here simply by wrapping the set of all members on the Customer level of the Customer hierarchy with the Unorder() function, so:
UNORDER([Customer].[Customer].[Customer].MEMBERS)
The following query now executes in 27 seconds on a cold cache:
WITH
MEMBER MEASURES.CUSTOMERSTODATE AS
COUNT(
NONEMPTY(
UNORDER([Customer].[Customer].[Customer].MEMBERS)
, {[Measures].[Internet Sales Amount]}
*
{NULL : [Date].[Calendar].CURRENTMEMBER})
)
SELECT
{MEASURES.CUSTOMERSTODATE}
ON COLUMNS,
[Date].[Calendar].[Date].MEMBERS
ON ROWS
FROM
[Adventure Works]
As far as I can tell, Unorder() only makes a difference on calculations when used in combination with NonEmpty(), and when it is used over a large set (here the set of customers has around 18000 members). If you have calculations like this I would recommend testing to see if Unorder() makes a difference - if it does, please leave a comment and let me know what you find!
Chris has been working with Microsoft BI tools since he started using beta 3 of OLAP Services back in the late 90s. Since then he has worked with Analysis Services in a number of roles (including three years spent with Microsoft Consulting Services) and he is now an independent consultant specialising in complex MDX, Analysis Services cube design and Analysis Services query performance problems. His company website can be found at http://www.crossjoin.co.uk and his blog can be found at http://cwebbbi.wordpress.com/ . |
Tags: mdx, performance