<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://sqlblog.com/utility/FeedStylesheets/atom.xsl" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><title type="html">Microsoft OLAP by Mosha Pasumansky</title><subtitle type="html" /><id>http://sqlblog.com/blogs/mosha/atom.aspx</id><link rel="alternate" type="text/html" href="http://sqlblog.com/blogs/mosha/default.aspx" /><link rel="self" type="application/atom+xml" href="http://sqlblog.com/blogs/mosha/atom.aspx" /><generator uri="http://communityserver.org" version="2.1.61129.1">Community Server</generator><updated>2007-09-19T09:50:18Z</updated><entry><title>The simpler MDX is – the better !</title><link rel="alternate" type="text/html" href="http://sqlblog.com/blogs/mosha/archive/2008/07/04/the-simpler-mdx-is-the-better.aspx" /><id>http://sqlblog.com/blogs/mosha/archive/2008/07/04/the-simpler-mdx-is-the-better.aspx</id><published>2008-07-05T02:25:18Z</published><updated>2008-07-05T02:25:18Z</updated><content type="html">&lt;p&gt;&lt;/p&gt;  &lt;p&gt;Whenever I am asked “What is the single most important advice to write efficient MDX”, I always give same answer: The simpler MDX you write – typically the better performance you will get. The reasoning behind this is straightforward: If MDX is simple, it will be easier for the engine to understand and therefore to optimize it. I know that we all tend to think that we can do all kinds of tricks and outsmart engine, and sometimes it is true, but the engine becomes smarter and smarter with every release, and especially in AS2008. So the tricks which could’ve helped before, actually could be hurting more than helping.&lt;/p&gt;  &lt;p&gt;And now, it seems that I am not the only one saying that ! Mark Whitehorn (my coauthor of “&lt;a href="http://www.amazon.com/Fast-Track-MDX-Mark-Whitehorn/dp/1846281741"&gt;Fast Track to MDX&lt;/a&gt;” book) together with Keith Burns have just published a technical whitepaper “&lt;a href="http://msdn.microsoft.com/en-us/library/cc719165(SQL.100).aspx"&gt;Best Practices for Data Warehousing with SQL Server 2008&lt;/a&gt;”. This seems to be a whitepaper focused specifically on the improvements in SQL 2008, and it includes section named “&lt;a name="_Toc198032349"&gt;Write simpler MDX without worrying about performance&lt;/a&gt;”. Nice title, and in addition it has explanation of block computation mode.&lt;/p&gt;  &lt;p&gt;I have really only couple of minor comments about it:&lt;/p&gt;  &lt;p&gt;- Block computation mode was &lt;strong&gt;not&lt;/strong&gt; introduced in AS2008, it existed in AS2005 (I have plenty of examples in my blogs). AS2008 though, extended applicability of block computation mode very significantly. &lt;/p&gt;  &lt;p&gt;- Description of calculated member says that it is “running total of two consecutive years”, but the expression is not for running total, but rather for regular total, and also there is nothing specific about years in it. It will work with Month, Quarter, Day etc just as well.&lt;/p&gt;  &lt;p&gt;- I didn’t really understand the following statement “In most hierarchical structures we know that if data is present for one cell in 2003, it will be there for all cells in 2003. The trip to see if there is data for the previous period only needs to be carried out once”. I think this statement is really misleading. If data is present for one cell in 2003 – there is absolutely no guarantee that it will be present for all (even for any other) cells in 2003. The diagrams just above this statement show that in fact most of the cells don’t have data. What I think authors tried to say was that MDX function PrevMember doesn’t need to be called for every cell, because [2004].PrevMember will always result in [2003], and AS2008 is smart enough to make this evaluation only once, and then compute the entire subcube of 2003 for non-empty cells only&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;- The example uses VISIBLE=&lt;strong&gt;2&lt;/strong&gt; property on calculated member. I wonder why…&lt;/p&gt;
&lt;BR&gt;&lt;div class = "shareblock"&gt;&lt;span class = "shareblockTitle"&gt;Share this post:&lt;/span&gt;&lt;span class = "shareblockLink"&gt; &lt;a href = "mailto:?subject=The simpler MDX is – the better !&amp;amp;body=Seen on SQLblog.com: %0A%0A%09The simpler MDX is – the better !%0A%0Ahttp://sqlblog.com/blogs/mosha/archive/2008/07/04/the-simpler-mdx-is-the-better.aspx" target="_blank" title = "Email The simpler MDX is – the better !"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://sqlblog.com/blogs/mosha/archive/2008/07/04/the-simpler-mdx-is-the-better.aspx&amp;amp;title=The+simpler+MDX+is+%e2%80%93+the+better+!" target="_blank" title = "Submit The simpler MDX is – the better ! to del.icio.us"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/07/04/the-simpler-mdx-is-the-better.aspx&amp;amp;phase=2" target="_blank" title = "Submit The simpler MDX is – the better ! to digg.com"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/07/04/the-simpler-mdx-is-the-better.aspx&amp;amp;title=The+simpler+MDX+is+%e2%80%93+the+better+!" target="_blank" title = "Submit The simpler MDX is – the better ! to reddit.com"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://sqlblog.com/blogs/mosha/archive/2008/07/04/the-simpler-mdx-is-the-better.aspx&amp;amp;title=The+simpler+MDX+is+%e2%80%93+the+better+!" target="_blank" title = "Submit The simpler MDX is – the better ! to DotNetKicks"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;mkt=en-us&amp;amp;url=http://sqlblog.com/blogs/mosha/archive/2008/07/04/the-simpler-mdx-is-the-better.aspx&amp;amp;title=The+simpler+MDX+is+%e2%80%93+the+better+!&amp;amp;;top=1" target="_blank" title = "Add The simpler MDX is – the better ! to Live Bookmarks"&gt;live it!&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;img src="http://info.sqlblog.com/a.aspx?ZoneID=0&amp;BannerID=12&amp;AdvertiserID=1&amp;CampaignID=12&amp;Task=Get&amp;Mode=TEXT&amp;SiteID=1&amp;RandomNumber=463323" width="1" height="1" border="0"&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=7667" width="1" height="1"&gt;</content><author><name>mosha</name><uri>http://sqlblog.com/members/mosha.aspx</uri></author></entry><entry><title>MDX Studio Online – Format and Parse MDX</title><link rel="alternate" type="text/html" href="http://sqlblog.com/blogs/mosha/archive/2008/07/01/mdx-formatting-and-parsing-on-the-web-with-mdx-studio-online-edition.aspx" /><id>http://sqlblog.com/blogs/mosha/archive/2008/07/01/mdx-formatting-and-parsing-on-the-web-with-mdx-studio-online-edition.aspx</id><published>2008-07-01T06:55:04Z</published><updated>2008-07-01T06:55:04Z</updated><content type="html">&lt;p&gt;MDX Formatting functionality &lt;a href="http://sqlblog.com/blogs/mosha/archive/2008/06/16/mdx-formatting-with-mdx-studio.aspx"&gt;debuted&lt;/a&gt; in MDX Studio 0.2.8, and judging by the amount of feedback I got on it – it became the most popular feature of MDX Studio ever ! Couple of people mentioned that for SQL formatting/beautifying needs, they would go online to sites like &lt;a href="http://www.sqlhere.com"&gt;www.sqlhere.com&lt;/a&gt;, which often do reasonable job for MDX as well. So this got me thinking that I could put MDX formatting functionality online as well. With little refactoring of MDX Studio code I was able to hack my first ever ASP.NET application, which became &lt;a href="http://mdx.mosha.com"&gt;MDX Studio Online Edition&lt;/a&gt;. This was a significant milestone, so I promoted version number to 0.3, and also promoted status from Alpha to Beta. &lt;/p&gt;  &lt;p&gt;The MDX Studio Online can be accessed at &lt;a href="http://mdx.mosha.com"&gt;http://mdx.mosha.com&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;In addition to “Format MDX” button, it also features “Parse” button, which does exactly the same as desktop version – it shows the MDX parse tree. Just like in desktop version, it can Format and Parse both queries (i.e. SELECT statements) and MDX Scripts (i.e. CREATE MEMBER/SET, SCOPE, assignments etc). &lt;strike&gt;The biggest known feature which is lacking from MDX Studio formatting – is support for comments. Currently it just ignores the comments instead of carrying them forward into formatted statements. I still didn’t figured out how to do it, but this issue tops my todo list&lt;/strike&gt;.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;&lt;strong&gt;Update:&lt;/strong&gt; Big thanks to Nick Medveditskov who also has written his own MDX Formatter application – he told me the trick he used to preserve comments in formatted MDX, and this functionality is now implemented in MDX Studio deployed online.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;Being an online application – you can use it right away, without any installation, so it can be especially handful for consultants working at customer site, where software installations are not allowed. Another great benefit of it being online application, is that everybody is always on the latest code, so all the hotfixes and version upgrades go live for everybody immediately (well the downside is that if I break something, then everybody is broken right away and there is no rollback, but this would never happen to me, of course, would it ?) &lt;/p&gt;  &lt;p&gt;I would like to thanks several people who did smoke testing for this application before it went public and provided lots of very valuable feedback and advice, people from all around the world – Vladimir Stepa (Germany), &lt;a href="http://www.ssas-info.com/VidasMatelisBlog/"&gt;Vidas Matelis&lt;/a&gt; (Canada), &lt;a&gt;Greg Galloway&lt;/a&gt; (US), &lt;a href="http://geekswithblogs.net/darrengosbell/"&gt;Darren Gosbell&lt;/a&gt; (Australia), &lt;a href="http://asmdx.blogspot.com/"&gt;Ajit Singh&lt;/a&gt; (India) and &lt;a href="http://cwebbbi.spaces.live.com"&gt;Chris Webb&lt;/a&gt; (UK). After all of them gave green light to the current version, I feel OK now to release it to public. &lt;/p&gt;  &lt;p&gt;As usual please send your feedback about the tool to the MDX Studio forum at &lt;a title="http://www.ssas-info.com/forum/MDXStudio" href="http://www.ssas-info.com/forum/MDXStudio"&gt;http://www.ssas-info.com/forum/MDXStudio&lt;/a&gt;&lt;/p&gt;
&lt;BR&gt;&lt;div class = "shareblock"&gt;&lt;span class = "shareblockTitle"&gt;Share this post:&lt;/span&gt;&lt;span class = "shareblockLink"&gt; &lt;a href = "mailto:?subject=MDX Studio Online – Format and Parse MDX&amp;amp;body=Seen on SQLblog.com: %0A%0A%09MDX Studio Online – Format and Parse MDX%0A%0Ahttp://sqlblog.com/blogs/mosha/archive/2008/07/01/mdx-formatting-and-parsing-on-the-web-with-mdx-studio-online-edition.aspx" target="_blank" title = "Email MDX Studio Online – Format and Parse MDX"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://sqlblog.com/blogs/mosha/archive/2008/07/01/mdx-formatting-and-parsing-on-the-web-with-mdx-studio-online-edition.aspx&amp;amp;title=MDX+Studio+Online+%e2%80%93+Format+and+Parse+MDX" target="_blank" title = "Submit MDX Studio Online – Format and Parse MDX to del.icio.us"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/07/01/mdx-formatting-and-parsing-on-the-web-with-mdx-studio-online-edition.aspx&amp;amp;phase=2" target="_blank" title = "Submit MDX Studio Online – Format and Parse MDX to digg.com"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/07/01/mdx-formatting-and-parsing-on-the-web-with-mdx-studio-online-edition.aspx&amp;amp;title=MDX+Studio+Online+%e2%80%93+Format+and+Parse+MDX" target="_blank" title = "Submit MDX Studio Online – Format and Parse MDX to reddit.com"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://sqlblog.com/blogs/mosha/archive/2008/07/01/mdx-formatting-and-parsing-on-the-web-with-mdx-studio-online-edition.aspx&amp;amp;title=MDX+Studio+Online+%e2%80%93+Format+and+Parse+MDX" target="_blank" title = "Submit MDX Studio Online – Format and Parse MDX to DotNetKicks"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;mkt=en-us&amp;amp;url=http://sqlblog.com/blogs/mosha/archive/2008/07/01/mdx-formatting-and-parsing-on-the-web-with-mdx-studio-online-edition.aspx&amp;amp;title=MDX+Studio+Online+%e2%80%93+Format+and+Parse+MDX&amp;amp;;top=1" target="_blank" title = "Add MDX Studio Online – Format and Parse MDX to Live Bookmarks"&gt;live it!&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;img src="http://info.sqlblog.com/a.aspx?ZoneID=0&amp;BannerID=12&amp;AdvertiserID=1&amp;CampaignID=12&amp;Task=Get&amp;Mode=TEXT&amp;SiteID=1&amp;RandomNumber=463323" width="1" height="1" border="0"&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=7589" width="1" height="1"&gt;</content><author><name>mosha</name><uri>http://sqlblog.com/members/mosha.aspx</uri></author></entry><entry><title>Analysis Services protocol – official documentation</title><link rel="alternate" type="text/html" href="http://sqlblog.com/blogs/mosha/archive/2008/06/30/analysis-services-protocol-official-documentation.aspx" /><id>http://sqlblog.com/blogs/mosha/archive/2008/06/30/analysis-services-protocol-official-documentation.aspx</id><published>2008-06-30T21:57:37Z</published><updated>2008-06-30T21:57:37Z</updated><content type="html">&lt;p&gt;&lt;/p&gt;  &lt;p&gt;One of my most popular blog posts was “&lt;a href="http://sqlblog.com/blogs/mosha/archive/2005/12/02/analysis-services-2005-protocol-xmla-over-tcp-ip.aspx"&gt;Analysis Services 2005 protocol - XMLA over TCP/IP&lt;/a&gt;”. Previously this information wasn’t available from anywhere else. But things changed today. Microsoft now has engaged in the “&lt;a href="http://msdn.microsoft.com/en-us/library/cc216514.aspx"&gt;Open Specification program&lt;/a&gt;”. Citing from the web page:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;Microsoft is providing open connections to its high-volume products—Windows Vista (including the .NET Framework), Windows Server 2008, SQL Server 2008, Office 2007, Exchange Server 2007, and Office SharePoint Server 2007—as well as additional information so that software developers, business partners and competitors can better interact with these Microsoft products or invent new solutions for customers.&lt;/em&gt;&lt;/p&gt;    &lt;p&gt;&lt;em&gt;As a developer, you now have full access to information about protocols, binary file formats, and other specifications for these products that can be used to create solutions.&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;(Yes, these include Office file format specifications!)&lt;/p&gt;  &lt;p&gt;Since Analysis Services is part of SQL Server, the protocols for accessing Analysis Services are listed under “&lt;a href="http://msdn.microsoft.com/en-us/library/cc678150.aspx"&gt;Microsoft SQL Server Protocols&lt;/a&gt;”. The most interesting one is &lt;/p&gt;  &lt;p&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/cc676961.aspx"&gt;[MS-SSAS9]: SQL Server Analysis Services Protocol Specification&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;which documents Analysis Services 2005 protocol (and 2008 as well, since protocol didn’t change between these versions). &lt;/p&gt;  &lt;p&gt;There is also&lt;/p&gt;  &lt;p&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/cc676956.aspx"&gt;[MS-SSAS8]: SQL Server Analysis Services Protocol Specification&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;which documents Analysis Services 2000 protocol. However, this one would be much harder to go through, since AS2000 had “smart client” architecture, where client code was doing lots of work – MDX parsing, evaluation, caching etc – therefore the protocol between client and server is at much lower level than MDX queries and it is not easy to make sense out of it.&lt;/p&gt;
&lt;BR&gt;&lt;div class = "shareblock"&gt;&lt;span class = "shareblockTitle"&gt;Share this post:&lt;/span&gt;&lt;span class = "shareblockLink"&gt; &lt;a href = "mailto:?subject=Analysis Services protocol – official documentation&amp;amp;body=Seen on SQLblog.com: %0A%0A%09Analysis Services protocol – official documentation%0A%0Ahttp://sqlblog.com/blogs/mosha/archive/2008/06/30/analysis-services-protocol-official-documentation.aspx" target="_blank" title = "Email Analysis Services protocol – official documentation"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://sqlblog.com/blogs/mosha/archive/2008/06/30/analysis-services-protocol-official-documentation.aspx&amp;amp;title=Analysis+Services+protocol+%e2%80%93+official+documentation" target="_blank" title = "Submit Analysis Services protocol – official documentation to del.icio.us"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/06/30/analysis-services-protocol-official-documentation.aspx&amp;amp;phase=2" target="_blank" title = "Submit Analysis Services protocol – official documentation to digg.com"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/06/30/analysis-services-protocol-official-documentation.aspx&amp;amp;title=Analysis+Services+protocol+%e2%80%93+official+documentation" target="_blank" title = "Submit Analysis Services protocol – official documentation to reddit.com"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://sqlblog.com/blogs/mosha/archive/2008/06/30/analysis-services-protocol-official-documentation.aspx&amp;amp;title=Analysis+Services+protocol+%e2%80%93+official+documentation" target="_blank" title = "Submit Analysis Services protocol – official documentation to DotNetKicks"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;mkt=en-us&amp;amp;url=http://sqlblog.com/blogs/mosha/archive/2008/06/30/analysis-services-protocol-official-documentation.aspx&amp;amp;title=Analysis+Services+protocol+%e2%80%93+official+documentation&amp;amp;;top=1" target="_blank" title = "Add Analysis Services protocol – official documentation to Live Bookmarks"&gt;live it!&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;img src="http://info.sqlblog.com/a.aspx?ZoneID=0&amp;BannerID=12&amp;AdvertiserID=1&amp;CampaignID=12&amp;Task=Get&amp;Mode=TEXT&amp;SiteID=1&amp;RandomNumber=463323" width="1" height="1" border="0"&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=7582" width="1" height="1"&gt;</content><author><name>mosha</name><uri>http://sqlblog.com/members/mosha.aspx</uri></author></entry><entry><title>MDX Formatting with MDX Studio</title><link rel="alternate" type="text/html" href="http://sqlblog.com/blogs/mosha/archive/2008/06/16/mdx-formatting-with-mdx-studio.aspx" /><id>http://sqlblog.com/blogs/mosha/archive/2008/06/16/mdx-formatting-with-mdx-studio.aspx</id><published>2008-06-16T23:20:23Z</published><updated>2008-06-16T23:20:23Z</updated><content type="html">&lt;p&gt;Darren Gosbell put up a &lt;a href="http://geekswithblogs.net/darrengosbell/archive/2008/06/15/a-rose-by-any-other-name---mdx-formatting.aspx"&gt;blog post&lt;/a&gt; about MDX Formatting, where he describes how he prefers to format MDX to look nice on the screen. So I thought – why to leave to a human something that machine can do. I decided to implement these rules (with some modifications) into MDX Studio. It became MDX Studio 0.2.8 release, available immediately for download at &lt;a title="http://cid-74f04d1ea28ece4e.skydrive.live.com/browse.aspx/MDXStudio/v0.2.8" href="http://cid-74f04d1ea28ece4e.skydrive.live.com/browse.aspx/MDXStudio/v0.2.8"&gt;http://cid-74f04d1ea28ece4e.skydrive.live.com/browse.aspx/MDXStudio/v0.2.8&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;I only had couple of hours to work on it, so it is rough on the edges, and I will appreciate comments in the MDX Studio forum at &lt;a title="http://www.ssas-info.com/forum/MDXStudio" href="http://www.ssas-info.com/forum/MDXStudio"&gt;http://www.ssas-info.com/forum/MDXStudio&lt;/a&gt;. But basic staff like calculated members, named sets, subselects etc should work. (One known exception is set aliases).&lt;/p&gt;  &lt;p&gt;The functionality is accessible through MDX –&amp;gt; Format menu. Here is example from Darren’s blog&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;with member &lt;/span&gt;&lt;span style="color:black;"&gt;measures&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;ptd &lt;/span&gt;&lt;span style="color:blue;"&gt;as &lt;/span&gt;&lt;span style="color:black;"&gt;'&lt;/span&gt;&lt;span style="color:darkred;"&gt;sum&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:darkred;"&gt;periodstodate&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Calendar]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Month]&lt;/span&gt;&lt;span style="color:gray;"&gt;,
&lt;/span&gt;&lt;span style="color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Calendar]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:darkred;"&gt;currentmember&lt;/span&gt;&lt;span style="color:gray;"&gt;),&lt;/span&gt;&lt;span style="color:black;"&gt;[Measures]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Sales Amount] &lt;/span&gt;&lt;span style="color:gray;"&gt;)&lt;/span&gt;&lt;span style="color:black;"&gt;'&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt;&lt;span style="color:black;"&gt;format_string &lt;/span&gt;&lt;span style="color:gray;"&gt;= &lt;/span&gt;&lt;span style="color:red;"&gt;&amp;quot;currency&amp;quot;
&lt;/span&gt;&lt;span style="color:blue;"&gt;select   &lt;/span&gt;&lt;span style="color:black;"&gt;{[Measures]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Measures]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Sales Amount] &lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt;&lt;span style="color:black;"&gt;measures&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;ptd} &lt;/span&gt;&lt;span style="color:blue;"&gt;on &lt;/span&gt;&lt;span style="color:magenta;"&gt;0&lt;/span&gt;&lt;span style="color:gray;"&gt;,
&lt;/span&gt;&lt;span style="color:black;"&gt;{[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Calendar]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Month]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;&amp;amp;[2003]&amp;amp;[2]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:darkred;"&gt;children&lt;/span&gt;&lt;span style="color:black;"&gt;} &lt;/span&gt;&lt;span style="color:blue;"&gt;on &lt;/span&gt;&lt;span style="color:magenta;"&gt;1 &lt;/span&gt;&lt;span style="color:blue;"&gt;from &lt;/span&gt;&lt;span style="color:black;"&gt;[Adventure Works] &lt;/span&gt;&lt;span style="color:blue;"&gt;where 
&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product Categories]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Category]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;&amp;amp;[1]&lt;/span&gt;&lt;span style="color:gray;"&gt;)
&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;And here is how it looks after autoformatting:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="background:white;color:black;"&gt;&lt;/span&gt;&lt;span style="background:white;color:blue;"&gt;WITH
  MEMBER &lt;/span&gt;&lt;span style="background:white;color:black;"&gt;measures&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:black;"&gt;ptd &lt;/span&gt;&lt;span style="background:white;color:blue;"&gt;AS 
    &lt;/span&gt;&lt;span style="background:white;color:darkred;"&gt;sum&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;(
      &lt;/span&gt;&lt;span style="background:white;color:darkred;"&gt;periodstodate&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;(
        &lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Calendar]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Month]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;,
        &lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Calendar]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:darkred;"&gt;currentmember&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;),
      &lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Measures]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Sales Amount]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;) 
    
    , &lt;/span&gt;&lt;span style="background:white;color:black;"&gt;format_string &lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;= &lt;/span&gt;&lt;span style="background:white;color:red;"&gt;&amp;quot;currency&amp;quot;
&lt;/span&gt;&lt;span style="background:white;color:blue;"&gt;SELECT
  &lt;/span&gt;&lt;span style="background:white;color:black;"&gt;{
    [Measures]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Measures]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Sales Amount]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;,
    &lt;/span&gt;&lt;span style="background:white;color:black;"&gt;measures&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:black;"&gt;ptd
  } &lt;/span&gt;&lt;span style="background:white;color:blue;"&gt;ON &lt;/span&gt;&lt;span style="background:white;color:magenta;"&gt;0&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;,
  &lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Calendar]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Month]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:black;"&gt;&amp;amp;[2003]&amp;amp;[2]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:darkred;"&gt;children &lt;/span&gt;&lt;span style="background:white;color:blue;"&gt;ON &lt;/span&gt;&lt;span style="background:white;color:magenta;"&gt;1
&lt;/span&gt;&lt;span style="background:white;color:blue;"&gt;FROM &lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Adventure Works]
&lt;/span&gt;&lt;span style="background:white;color:blue;"&gt;WHERE 
  &lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Product Categories]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:black;"&gt;[Category]&lt;/span&gt;&lt;span style="background:white;color:gray;"&gt;.&lt;/span&gt;&lt;span style="background:white;color:black;"&gt;&amp;amp;[1]
&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;
&lt;BR&gt;&lt;div class = "shareblock"&gt;&lt;span class = "shareblockTitle"&gt;Share this post:&lt;/span&gt;&lt;span class = "shareblockLink"&gt; &lt;a href = "mailto:?subject=MDX Formatting with MDX Studio&amp;amp;body=Seen on SQLblog.com: %0A%0A%09MDX Formatting with MDX Studio%0A%0Ahttp://sqlblog.com/blogs/mosha/archive/2008/06/16/mdx-formatting-with-mdx-studio.aspx" target="_blank" title = "Email MDX Formatting with MDX Studio"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://sqlblog.com/blogs/mosha/archive/2008/06/16/mdx-formatting-with-mdx-studio.aspx&amp;amp;title=MDX+Formatting+with+MDX+Studio" target="_blank" title = "Submit MDX Formatting with MDX Studio to del.icio.us"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/06/16/mdx-formatting-with-mdx-studio.aspx&amp;amp;phase=2" target="_blank" title = "Submit MDX Formatting with MDX Studio to digg.com"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/06/16/mdx-formatting-with-mdx-studio.aspx&amp;amp;title=MDX+Formatting+with+MDX+Studio" target="_blank" title = "Submit MDX Formatting with MDX Studio to reddit.com"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://sqlblog.com/blogs/mosha/archive/2008/06/16/mdx-formatting-with-mdx-studio.aspx&amp;amp;title=MDX+Formatting+with+MDX+Studio" target="_blank" title = "Submit MDX Formatting with MDX Studio to DotNetKicks"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;mkt=en-us&amp;amp;url=http://sqlblog.com/blogs/mosha/archive/2008/06/16/mdx-formatting-with-mdx-studio.aspx&amp;amp;title=MDX+Formatting+with+MDX+Studio&amp;amp;;top=1" target="_blank" title = "Add MDX Formatting with MDX Studio to Live Bookmarks"&gt;live it!&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;img src="http://info.sqlblog.com/a.aspx?ZoneID=0&amp;BannerID=12&amp;AdvertiserID=1&amp;CampaignID=12&amp;Task=Get&amp;Mode=TEXT&amp;SiteID=1&amp;RandomNumber=463323" width="1" height="1" border="0"&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=7329" width="1" height="1"&gt;</content><author><name>mosha</name><uri>http://sqlblog.com/members/mosha.aspx</uri></author><category term="mdx studio" scheme="http://sqlblog.com/blogs/mosha/archive/tags/mdx+studio/default.aspx" /></entry><entry><title>No OLAP Market shares for 2007</title><link rel="alternate" type="text/html" href="http://sqlblog.com/blogs/mosha/archive/2008/06/16/no-olap-market-shares-for-2007.aspx" /><id>http://sqlblog.com/blogs/mosha/archive/2008/06/16/no-olap-market-shares-for-2007.aspx</id><published>2008-06-16T16:12:50Z</published><updated>2008-06-16T16:12:50Z</updated><content type="html">&lt;p&gt;Every year around March or April I am looking forward the &lt;a href="http://www.olapreport.com/"&gt;OLAP Report&lt;/a&gt; site to see the OLAP market share numbers. Computing these numbers is not a simple task as Nigel Pendse explains on &lt;a title="http://www.olapreport.com/market.htm" href="http://www.olapreport.com/market.htm"&gt;http://www.olapreport.com/market.htm&lt;/a&gt;, and this kind of analysis really is not available anywhere else. This year again, I was waiting for the 2007 results to see by how much Microsoft increased its market share. March and April passed, then May passed, we are well into June, and the results didn’t show up. So I contacted Nigel, and here is what he told me (publishing with permission):&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;Mosha,&lt;/em&gt;&lt;/p&gt;    &lt;p&gt;&lt;em&gt;Unfortunately, I don't think it's possible to calculate accurate OLAP market shares any more. Applix, Business Objects, Cognos and Hyperion were all acquired in the last year, and therefore no longer publish figures, so it's no longer possible to work out their OLAP revenues. Furthermore, in the year of acquisition, their figures are particularly distorted (sales people work very hard to close deals just before the acquisition, because they don't expect to keep the commission afterwards, so sales are high the quarter before the deals close, and weak the following quarter).&lt;/em&gt;&lt;/p&gt;    &lt;p&gt;&lt;em&gt;Of, course, the disruption doesn't help sales in those companies, so I don't doubt that Microsoft increased its lead, its just that I don't have any good way any longer of calculating it. Generally, the new owners of those companies are not prepared to disclose product revenues.&lt;/em&gt;&lt;/p&gt;    &lt;p&gt;&lt;em&gt;Regards&lt;/em&gt;&lt;/p&gt;    &lt;p&gt;&lt;em&gt;Nigel&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;So after the wave of last year consolidations, it is no longer possible to have accurate estimates. But even without precise numbers, how the future looks like ? (emphasis is mine)&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;I may try again in the future, but at the moment, I can't think of a reliable way to calculate OLAP market shares. &lt;/em&gt;&lt;em&gt;I think it's also less interesting than in the past, with fewer significant vendors remaining. &lt;/em&gt;&lt;/p&gt;    &lt;p&gt;&lt;em&gt;&lt;strong&gt;Microsoft is clearly going to stay #1&lt;/strong&gt;. &lt;/em&gt;&lt;/p&gt;    &lt;p&gt;&lt;em&gt;Oracle+Hyperion will probably stay at #2, but its share is unlikely to rise.&lt;/em&gt;&lt;/p&gt;    &lt;p&gt;&lt;em&gt;IBM (Cognos+Applix) will stay at number 3; its share may rise a bit, particularly if IBM gives TM1 the promotion that Applix could not afford to do, but I think IBM will stay in #3. &lt;/em&gt;&lt;/p&gt;    &lt;p&gt;&lt;em&gt;SAP+BOBJ will stay in the next position.&lt;/em&gt;&lt;/p&gt;    &lt;p&gt;&lt;em&gt;MicroStrategy may gain share, but is unlikely to overtake SAP+BOBJ. &lt;/em&gt;&lt;/p&gt;    &lt;p&gt;&lt;em&gt;No other vendor comes close, so the top five aren't likely to change position any time soon.&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Anyway, the picture below shows how the market share trends looked like until 2006 (source: OLAP Report)&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.olapreport.com/images/MarketShareTrend2006.png" /&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Calculating the market shares for consolidated companies based on 2006 positions&lt;/p&gt;  &lt;table cellspacing="0" cellpadding="2"&gt;     &lt;tr&gt;       &lt;td&gt;&amp;#160;&lt;/td&gt;        &lt;td&gt;2006 Market share&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td&gt;Microsoft&lt;/td&gt;        &lt;td&gt;31.6%&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td&gt;Oracle+Hyperion&lt;/td&gt;        &lt;td&gt;21.7%&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td&gt;Cognos+Applix=IBM&lt;/td&gt;        &lt;td&gt;16.5%&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td&gt;SAP+Business Objects&lt;/td&gt;        &lt;td&gt;13.1%&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td&gt;Microstrategy&lt;/td&gt;        &lt;td&gt;7.3%&lt;/td&gt;     &lt;/tr&gt;   &lt;/table&gt;  &lt;p&gt;&lt;strong&gt;Update: &lt;/strong&gt;Turns out the numbers in this table are not accurate, because I forgot to account that Business Objects had acquired Cartesis and that SAP had acquired OutlookSoft. Anyway, Nigel has updated his &lt;a href="http://www.olapreport.com/market.htm"&gt;OLAP market share&lt;/a&gt; page with deeper analysis of 2007 consolidation effect on market shares, and he has more accurate numbers.&lt;/p&gt;
&lt;BR&gt;&lt;div class = "shareblock"&gt;&lt;span class = "shareblockTitle"&gt;Share this post:&lt;/span&gt;&lt;span class = "shareblockLink"&gt; &lt;a href = "mailto:?subject=No OLAP Market shares for 2007&amp;amp;body=Seen on SQLblog.com: %0A%0A%09No OLAP Market shares for 2007%0A%0Ahttp://sqlblog.com/blogs/mosha/archive/2008/06/16/no-olap-market-shares-for-2007.aspx" target="_blank" title = "Email No OLAP Market shares for 2007"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://sqlblog.com/blogs/mosha/archive/2008/06/16/no-olap-market-shares-for-2007.aspx&amp;amp;title=No+OLAP+Market+shares+for+2007" target="_blank" title = "Submit No OLAP Market shares for 2007 to del.icio.us"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/06/16/no-olap-market-shares-for-2007.aspx&amp;amp;phase=2" target="_blank" title = "Submit No OLAP Market shares for 2007 to digg.com"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/06/16/no-olap-market-shares-for-2007.aspx&amp;amp;title=No+OLAP+Market+shares+for+2007" target="_blank" title = "Submit No OLAP Market shares for 2007 to reddit.com"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://sqlblog.com/blogs/mosha/archive/2008/06/16/no-olap-market-shares-for-2007.aspx&amp;amp;title=No+OLAP+Market+shares+for+2007" target="_blank" title = "Submit No OLAP Market shares for 2007 to DotNetKicks"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;mkt=en-us&amp;amp;url=http://sqlblog.com/blogs/mosha/archive/2008/06/16/no-olap-market-shares-for-2007.aspx&amp;amp;title=No+OLAP+Market+shares+for+2007&amp;amp;;top=1" target="_blank" title = "Add No OLAP Market shares for 2007 to Live Bookmarks"&gt;live it!&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;img src="http://info.sqlblog.com/a.aspx?ZoneID=0&amp;BannerID=12&amp;AdvertiserID=1&amp;CampaignID=12&amp;Task=Get&amp;Mode=TEXT&amp;SiteID=1&amp;RandomNumber=463323" width="1" height="1" border="0"&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=7320" width="1" height="1"&gt;</content><author><name>mosha</name><uri>http://sqlblog.com/members/mosha.aspx</uri></author></entry><entry><title>Counting distinct values in MDX</title><link rel="alternate" type="text/html" href="http://sqlblog.com/blogs/mosha/archive/2008/06/14/counting-distinct-values-in-mdx.aspx" /><id>http://sqlblog.com/blogs/mosha/archive/2008/06/14/counting-distinct-values-in-mdx.aspx</id><published>2008-06-15T00:13:01Z</published><updated>2008-06-15T00:13:01Z</updated><content type="html">&lt;p&gt;How many elements in the set have distinct values ? This sounds like a simple question. Apparently, this is well known problem in Excel, and there is a classic solution for it. There are plenty of sites in Internet (for example &lt;a href="http://www.mrexcel.com/archive/Formulas/18882.html"&gt;here&lt;/a&gt; and &lt;a href="http://www.j-walk.com/ss/excel/usertips/tip061.htm"&gt;here&lt;/a&gt;) which give the following solution:&lt;/p&gt;  &lt;pre&gt;=SUM(1/COUNTIF(A1:A6,A1:A6)))&lt;/pre&gt;

&lt;p&gt;(note that this formula needs to be entered with Ctrl-Shift-Enter to indicate that this is array formula, or it won’t give right result – one of the Excel quirks)&lt;/p&gt;

&lt;p&gt;When someone showed me this solution, I just stared at it and I couldn’t understand how it worked. Only rewriting it in MDX clarified what was going on. Let’s take the following example based on Adventure Works – we want to count how different letters the product names start from (just wanted to pick something for which Product dimension didn’t have dedicated attribute, because otherwise it would’ve been too simple). The MDX which performs the same algorithm as Excel formula above in these conditions will look like following:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;with 
  member measures.&lt;/span&gt;&lt;span style="color:black;"&gt;CountDistinctLetters &lt;/span&gt;&lt;span style="color:blue;"&gt;as 
   &lt;/span&gt;&lt;span style="color:darkred;"&gt;Sum&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:blue;"&gt;MEMBERS AS &lt;/span&gt;&lt;span style="color:black;"&gt;AllProducts&lt;/span&gt;&lt;span style="color:gray;"&gt;,
       &lt;/span&gt;&lt;span style="color:magenta;"&gt;1&lt;/span&gt;&lt;span style="color:gray;"&gt;/&lt;/span&gt;&lt;span style="color:darkred;"&gt;Filter&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:blue;"&gt;MEMBERS&lt;/span&gt;&lt;span style="color:gray;"&gt;, 
          &lt;/span&gt;&lt;span style="color:black;"&gt;VBA!Left&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;AllProducts&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:darkred;"&gt;Current&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:blue;"&gt;Name&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt;&lt;span style="color:magenta;"&gt;1&lt;/span&gt;&lt;span style="color:gray;"&gt;) = &lt;/span&gt;&lt;span style="color:black;"&gt;VBA!Left&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:darkred;"&gt;CurrentMember&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:blue;"&gt;Name&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt;&lt;span style="color:magenta;"&gt;1&lt;/span&gt;&lt;span style="color:gray;"&gt;)
         ).&lt;/span&gt;&lt;span style="color:blue;"&gt;Count&lt;/span&gt;&lt;span style="color:gray;"&gt;)

&lt;/span&gt;&lt;span style="color:blue;"&gt;select &lt;/span&gt;&lt;span style="color:black;"&gt;CountDistinctLetters &lt;/span&gt;&lt;span style="color:blue;"&gt;on &lt;/span&gt;&lt;span style="color:magenta;"&gt;0
&lt;/span&gt;&lt;span style="color:blue;"&gt;from &lt;/span&gt;&lt;span style="color:black;"&gt;[Adventure Works]
&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;It is more verbose, but probably it is not simpler to understand to normal developer than the Excel’s formula. In the nutshell the idea is following: For every element in the set, we compute how many other elements produce the same value (this is COUNTIF in Excel, and Filter(…).Count in MDX), and then dividing 1 by this number we get fraction of current element. The summing up all these fractions will give the desired number. &lt;/p&gt;

&lt;p&gt;(Unfortunately there is no easy way to debug such formulas. The MDX debugger inside &lt;a href="http://www.mosha.com/msolap/mdxstudio.htm"&gt;MDX Studio&lt;/a&gt; is not capable of dealing with such expressions. I actually spent quite a time thinking about how to build decent environment for debugging MDX, and after few failed starts I had what I consider as breakthrough idea. Early experiments were very encouraging, and if this idea works, I will blog soon about it. It radically different from all other MDX debugging attempts.)&lt;/p&gt;

&lt;p&gt;It is clear that performance of such solution is horrible. For every element in the set, we go again and again over all elements in the set just to find out how many of them have same value. The algorithmic complexity of this nested loop approach is &lt;em&gt;O(n^2)&lt;/em&gt;. Indeed the query above executes for 12 seconds. It is possible to slightly improve it, by forcing caching the results of VBA!Left through&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;with 
  member &lt;/span&gt;&lt;span style="color:black;"&gt;Prefix &lt;/span&gt;&lt;span style="color:blue;"&gt;as &lt;/span&gt;&lt;span style="color:black;"&gt;VBA!Left&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:darkred;"&gt;CurrentMember&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:blue;"&gt;Name&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt;&lt;span style="color:magenta;"&gt;1&lt;/span&gt;&lt;span style="color:gray;"&gt;)
  &lt;/span&gt;&lt;span style="color:blue;"&gt;member &lt;/span&gt;&lt;span style="color:black;"&gt;measures&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;CountDistinctLetters &lt;/span&gt;&lt;span style="color:blue;"&gt;as 
   &lt;/span&gt;&lt;span style="color:darkred;"&gt;Sum&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:blue;"&gt;MEMBERS AS &lt;/span&gt;&lt;span style="color:black;"&gt;AllProducts&lt;/span&gt;&lt;span style="color:gray;"&gt;,
       &lt;/span&gt;&lt;span style="color:magenta;"&gt;1&lt;/span&gt;&lt;span style="color:gray;"&gt;/&lt;/span&gt;&lt;span style="color:darkred;"&gt;Filter&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:blue;"&gt;MEMBERS&lt;/span&gt;&lt;span style="color:gray;"&gt;, 
          (&lt;/span&gt;&lt;span style="color:black;"&gt;AllProducts&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:darkred;"&gt;Current&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt;&lt;span style="color:black;"&gt;Prefix&lt;/span&gt;&lt;span style="color:gray;"&gt;) = &lt;/span&gt;&lt;span style="color:black;"&gt;Prefix
         &lt;/span&gt;&lt;span style="color:gray;"&gt;).&lt;/span&gt;&lt;span style="color:blue;"&gt;Count&lt;/span&gt;&lt;span style="color:gray;"&gt;)

&lt;/span&gt;&lt;span style="color:blue;"&gt;select &lt;/span&gt;&lt;span style="color:black;"&gt;CountDistinctLetters &lt;/span&gt;&lt;span style="color:blue;"&gt;on &lt;/span&gt;&lt;span style="color:magenta;"&gt;0
&lt;/span&gt;&lt;span style="color:blue;"&gt;from &lt;/span&gt;&lt;span style="color:black;"&gt;[Adventure Works]&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;and reducing time to 11 seconds, but obviously, different, more sane approach is needed here.&lt;/p&gt;

&lt;p&gt;Another idea is instead of doing nested-loop algorithm, sort the elements of the set, and then scan sorted set once counting number of different elements. The only technical difficulty with that would be - while scaning sorted set, we need to compare current element with the previous one. Getting current element is easy, but how to get previous one ? There is no &amp;quot;Previous&amp;quot; function in MDX, there is only PrevMember, but it works on hierarchical ordering of the level, and here we have arbitrary set sorted by arbitrary criteria. The solution is to use CurrentOrdinal function, which returns the index of the tuple being iterated in the set, and then using CurrentOrdinal-1 as a new index, send it to Item function. In MDX this would be recorded as &lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;with
  member &lt;/span&gt;&lt;span style="color:black;"&gt;Prefix &lt;/span&gt;&lt;span style="color:blue;"&gt;as &lt;/span&gt;&lt;span style="color:black;"&gt;VBA!Left&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:darkred;"&gt;CurrentMember&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:blue;"&gt;Name&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt;&lt;span style="color:magenta;"&gt;1&lt;/span&gt;&lt;span style="color:gray;"&gt;)
  &lt;/span&gt;&lt;span style="color:blue;"&gt;set &lt;/span&gt;&lt;span style="color:black;"&gt;ProductsSortedByPrefix &lt;/span&gt;&lt;span style="color:blue;"&gt;as &lt;/span&gt;&lt;span style="color:darkred;"&gt;order&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:blue;"&gt;MEMBERS&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:black;"&gt;Prefix&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:blue;"&gt;BASC&lt;/span&gt;&lt;span style="color:gray;"&gt;)
  &lt;/span&gt;&lt;span style="color:blue;"&gt;member &lt;/span&gt;&lt;span style="color:black;"&gt;measures&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;CountDistinctLetters  &lt;/span&gt;&lt;span style="color:blue;"&gt;as &lt;/span&gt;&lt;span style="color:darkred;"&gt;Sum&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;ProductsSortedByPrefix &lt;/span&gt;&lt;span style="color:blue;"&gt;as &lt;/span&gt;&lt;span style="color:black;"&gt;y&lt;/span&gt;&lt;span style="color:gray;"&gt;, 
    &lt;/span&gt;&lt;span style="color:darkred;"&gt;iif&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;y&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:darkred;"&gt;CurrentOrdinal &lt;/span&gt;&lt;span style="color:gray;"&gt;= &lt;/span&gt;&lt;span style="color:magenta;"&gt;1 &lt;/span&gt;&lt;span style="color:blue;"&gt;OR &lt;/span&gt;&lt;span style="color:black;"&gt;Prefix &lt;/span&gt;&lt;span style="color:gray;"&gt;&amp;lt;&amp;gt; (&lt;/span&gt;&lt;span style="color:black;"&gt;Prefix&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:black;"&gt;y&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:blue;"&gt;Item&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;y&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:darkred;"&gt;CurrentOrdinal&lt;/span&gt;&lt;span style="color:gray;"&gt;-&lt;/span&gt;&lt;span style="color:magenta;"&gt;2&lt;/span&gt;&lt;span style="color:gray;"&gt;)), &lt;/span&gt;&lt;span style="color:magenta;"&gt;1&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:magenta;"&gt;0&lt;/span&gt;&lt;span style="color:gray;"&gt;))

&lt;/span&gt;&lt;span style="color:blue;"&gt;select &lt;/span&gt;&lt;span style="color:black;"&gt;CountDistinctLetters  &lt;/span&gt;&lt;span style="color:blue;"&gt;on &lt;/span&gt;&lt;span style="color:magenta;"&gt;0
&lt;/span&gt;&lt;span style="color:blue;"&gt;from &lt;/span&gt;&lt;span style="color:black;"&gt;[Adventure Works]&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;This query executes much faster now, in only 0.234 seconds. The algorithmic complexity is down to &lt;em&gt;O(n*log(n))&lt;/em&gt;. However, it is still not perfect solution, since it is possible to solve this in just one scan of the original set, i.e. with &lt;em&gt;O(n)&lt;/em&gt; complexity. In order to do that, we will have to write stored procedure though. &lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;public int &lt;/span&gt;CountDistinctValues(&lt;span style="color:#2b91af;"&gt;Set &lt;/span&gt;set, &lt;span style="color:#2b91af;"&gt;Expression &lt;/span&gt;exp)
{
    System.Collections.&lt;span style="color:#2b91af;"&gt;Hashtable &lt;/span&gt;ht = &lt;span style="color:blue;"&gt;new &lt;/span&gt;System.Collections.&lt;span style="color:#2b91af;"&gt;Hashtable&lt;/span&gt;();
    &lt;span style="color:blue;"&gt;foreach &lt;/span&gt;(&lt;span style="color:#2b91af;"&gt;Tuple &lt;/span&gt;t &lt;span style="color:blue;"&gt;in &lt;/span&gt;set.Tuples)
    {
        &lt;span style="color:blue;"&gt;string &lt;/span&gt;v = exp.Calculate(t).ToString();
        &lt;span style="color:blue;"&gt;if &lt;/span&gt;( !ht.ContainsKey(v) )
            ht.Add(v, &lt;span style="color:blue;"&gt;null&lt;/span&gt;);
    }

    &lt;span style="color:blue;"&gt;return &lt;/span&gt;ht.Count;
}&lt;/pre&gt;

&lt;p&gt;Now, if we use this stored procedure in the query as following&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;with member &lt;/span&gt;&lt;span style="color:black;"&gt;CountDistinctLetters &lt;/span&gt;&lt;span style="color:blue;"&gt;as &lt;/span&gt;&lt;span style="color:black;"&gt;ASSP&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;ASStoredProcs&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;Util&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;CountDistinctValues&lt;/span&gt;&lt;span style="color:gray;"&gt;(
  &lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:blue;"&gt;MEMBERS&lt;/span&gt;&lt;span style="color:gray;"&gt;,
  &lt;/span&gt;&lt;span style="color:black;"&gt;VBA!Left&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:darkred;"&gt;CurrentMember&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:blue;"&gt;Name&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt;&lt;span style="color:magenta;"&gt;1&lt;/span&gt;&lt;span style="color:gray;"&gt;))
&lt;/span&gt;&lt;span style="color:blue;"&gt;select &lt;/span&gt;&lt;span style="color:black;"&gt;CountDistinctLetters &lt;/span&gt;&lt;span style="color:blue;"&gt;on &lt;/span&gt;&lt;span style="color:magenta;"&gt;0
&lt;/span&gt;&lt;span style="color:blue;"&gt;from &lt;/span&gt;&lt;span style="color:black;"&gt;[Adventure Works] &lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;&lt;span style="color:black;"&gt;we get it working in only 0.062 seconds.&lt;/span&gt;&lt;/p&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;
&lt;BR&gt;&lt;div class = "shareblock"&gt;&lt;span class = "shareblockTitle"&gt;Share this post:&lt;/span&gt;&lt;span class = "shareblockLink"&gt; &lt;a href = "mailto:?subject=Counting distinct values in MDX&amp;amp;body=Seen on SQLblog.com: %0A%0A%09Counting distinct values in MDX%0A%0Ahttp://sqlblog.com/blogs/mosha/archive/2008/06/14/counting-distinct-values-in-mdx.aspx" target="_blank" title = "Email Counting distinct values in MDX"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://sqlblog.com/blogs/mosha/archive/2008/06/14/counting-distinct-values-in-mdx.aspx&amp;amp;title=Counting+distinct+values+in+MDX" target="_blank" title = "Submit Counting distinct values in MDX to del.icio.us"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/06/14/counting-distinct-values-in-mdx.aspx&amp;amp;phase=2" target="_blank" title = "Submit Counting distinct values in MDX to digg.com"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/06/14/counting-distinct-values-in-mdx.aspx&amp;amp;title=Counting+distinct+values+in+MDX" target="_blank" title = "Submit Counting distinct values in MDX to reddit.com"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://sqlblog.com/blogs/mosha/archive/2008/06/14/counting-distinct-values-in-mdx.aspx&amp;amp;title=Counting+distinct+values+in+MDX" target="_blank" title = "Submit Counting distinct values in MDX to DotNetKicks"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;mkt=en-us&amp;amp;url=http://sqlblog.com/blogs/mosha/archive/2008/06/14/counting-distinct-values-in-mdx.aspx&amp;amp;title=Counting+distinct+values+in+MDX&amp;amp;;top=1" target="_blank" title = "Add Counting distinct values in MDX to Live Bookmarks"&gt;live it!&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;img src="http://info.sqlblog.com/a.aspx?ZoneID=0&amp;BannerID=12&amp;AdvertiserID=1&amp;CampaignID=12&amp;Task=Get&amp;Mode=TEXT&amp;SiteID=1&amp;RandomNumber=463323" width="1" height="1" border="0"&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=7296" width="1" height="1"&gt;</content><author><name>mosha</name><uri>http://sqlblog.com/members/mosha.aspx</uri></author><category term="mdx" scheme="http://sqlblog.com/blogs/mosha/archive/tags/mdx/default.aspx" /><category term="stored procedure" scheme="http://sqlblog.com/blogs/mosha/archive/tags/stored+procedure/default.aspx" /></entry><entry><title>BI Survey 8</title><link rel="alternate" type="text/html" href="http://sqlblog.com/blogs/mosha/archive/2008/06/13/bi-survey-8.aspx" /><id>http://sqlblog.com/blogs/mosha/archive/2008/06/13/bi-survey-8.aspx</id><published>2008-06-14T05:28:00Z</published><updated>2008-06-14T05:28:00Z</updated><content type="html">&lt;P&gt;BI Survey 8 is up and ready to accept participants. It’s hard to believe that we are at version 8 of what started as OLAP Survey 1 back in 2000. (On the other hand, my daughter also recently turned 8, she was born in 2000 too, and I still cannot believe that she is so big now, just finished second grade, it seems only yesterday she was a little baby). Anyway, over these years the OLAP/BI Survey has grown and matured, and every year many people including myself await the results and then carefully study them. So I encourage everybody to fill it, whether you use Microsoft OLAP, or some other OLAP system, whether you are happy or dissatisfied with it. More inputs – the better. Below is the official invitation from Nigel and Co:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;We would very much welcome your participation in The BI Survey, conducted annually by Nigel Pendse. This is the largest independent survey of business intelligence/OLAP users&lt;I&gt; &lt;/I&gt;worldwide&lt;I&gt;.&lt;/I&gt; The Survey will obtain input from a large number of organizations to better understand their buying decisions, the implementation cycle and the business success achieved. Both business and technical users, as well as vendors and consultants, are welcome. &lt;/P&gt;
&lt;P&gt;The BI Survey is strictly independent. While Microsoft and other vendors assist by inviting users to participate in the Survey, the vendors do not sponsor the survey, nor influence the questionnaire design or survey results. You will be able to answer questions on your usage of a BI product from any vendor. Your data will only be used anonymously, and no personal details will be passed to vendors or other third parties.&lt;/P&gt;
&lt;P&gt;As a participant, you will not only have the opportunity to ensure your experiences are included in the analyses, but you will also receive a summary of the results from the full survey. You will also have a chance of winning one of ten $50 Amazon vouchers. Click here to complete the survey on-line. &lt;/P&gt;
&lt;P&gt;English version: &lt;A href="http://www.eu-survey.com/BI_8/BI.asp?lan=1&amp;amp;lin=22"&gt;http://www.eu-survey.com/BI_8/BI.asp?lan=1&amp;amp;lin=22&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;German version: &lt;A href="http://www.eu-survey.com/BI_8/BI.asp?lan=2&amp;amp;lin=120"&gt;http://www.eu-survey.com/BI_8/BI.asp?lan=2&amp;amp;lin=120&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;Spanish version: &lt;A href="http://www.eu-survey.com/BI_8/BI.asp?lan=3&amp;amp;lin=213"&gt;http://www.eu-survey.com/BI_8/BI.asp?lan=3&amp;amp;lin=213&lt;/A&gt;&lt;A href="http://www.eu-survey.com/BI_8/BI.asp?lan=3"&gt;&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;BR&gt;&lt;div class = "shareblock"&gt;&lt;span class = "shareblockTitle"&gt;Share this post:&lt;/span&gt;&lt;span class = "shareblockLink"&gt; &lt;a href = "mailto:?subject=BI Survey 8&amp;amp;body=Seen on SQLblog.com: %0A%0A%09BI Survey 8%0A%0Ahttp://sqlblog.com/blogs/mosha/archive/2008/06/13/bi-survey-8.aspx" target="_blank" title = "Email BI Survey 8"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://sqlblog.com/blogs/mosha/archive/2008/06/13/bi-survey-8.aspx&amp;amp;title=BI+Survey+8" target="_blank" title = "Submit BI Survey 8 to del.icio.us"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/06/13/bi-survey-8.aspx&amp;amp;phase=2" target="_blank" title = "Submit BI Survey 8 to digg.com"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/06/13/bi-survey-8.aspx&amp;amp;title=BI+Survey+8" target="_blank" title = "Submit BI Survey 8 to reddit.com"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://sqlblog.com/blogs/mosha/archive/2008/06/13/bi-survey-8.aspx&amp;amp;title=BI+Survey+8" target="_blank" title = "Submit BI Survey 8 to DotNetKicks"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;mkt=en-us&amp;amp;url=http://sqlblog.com/blogs/mosha/archive/2008/06/13/bi-survey-8.aspx&amp;amp;title=BI+Survey+8&amp;amp;;top=1" target="_blank" title = "Add BI Survey 8 to Live Bookmarks"&gt;live it!&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;img src="http://info.sqlblog.com/a.aspx?ZoneID=0&amp;BannerID=12&amp;AdvertiserID=1&amp;CampaignID=12&amp;Task=Get&amp;Mode=TEXT&amp;SiteID=1&amp;RandomNumber=463323" width="1" height="1" border="0"&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=7284" width="1" height="1"&gt;</content><author><name>mosha</name><uri>http://sqlblog.com/members/mosha.aspx</uri></author></entry><entry><title>Follow up on NextAnalytics challenge</title><link rel="alternate" type="text/html" href="http://sqlblog.com/blogs/mosha/archive/2008/06/10/follow-up-on-nextanalytics-challenge.aspx" /><id>http://sqlblog.com/blogs/mosha/archive/2008/06/10/follow-up-on-nextanalytics-challenge.aspx</id><published>2008-06-11T05:28:00Z</published><updated>2008-06-11T05:28:00Z</updated><content type="html">&lt;h1&gt;&lt;/h1&gt;  &lt;p&gt;My post “&lt;a href="http://sqlblog.com/blogs/mosha/archive/2008/06/08/mdx-answer-to-nextanalytics-challenge.aspx"&gt;MDX answer to NextAnalytics challenge&lt;/a&gt;” triggered some very active discussion, and I would like to address some of the comments&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://cwebbbi.spaces.live.com/"&gt;&lt;em&gt;Chris Webb&lt;/em&gt;&lt;/a&gt;&lt;em&gt; said:        &lt;br /&gt;I've done a fair amount of teaching MDX and the truth is that people do find even the basics very difficult to understand, but my feeling is that the problem people have with MDX is not the language as such but thinking a) multidimensionally and b) in sets. The question is then, does Ward's new language solve either of these problems? Or has he reinvented the wheel?&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;I agree with the a) part, but not with the b) part. Thinking multidimensional is hard and take some time to get used to. However thinking in sets is not hard at all. SQL language is mostly based on relational algebra and uses sets extensively. Practitioners don’t find sets difficult. &lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://thomasianalytics.spaces.live.com/"&gt;&lt;em&gt;Thomas Ivarsson&lt;/em&gt;&lt;/a&gt;&lt;em&gt; said:        &lt;br /&gt;Can you publish the entire script at the end of this blog post? My conclusion is that we need better clients that can create these analytics sets at run time. End users will never do this and it is impossible to create all sets that the business users would like to have. It will fill up a cube in no time.&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;I am pessimistic that we will ever see such sophisticated clients. If they were in demand, we would’ve already had them. I am afraid the users will have to code such analytics either using MDX or NextAnalytics script or something else. End users don’t have any problem building such sets in Excel, so maybe Excel is our hope ?&lt;/p&gt;  &lt;p&gt;Here is the entire script:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// Nextanalytics challenge

// calculate the rolling six month average 
&lt;/span&gt;&lt;span style="color:blue;"&gt;CREATE &lt;/span&gt;&lt;span style="color:black;"&gt;Rolling6MonthAverage;
&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;Rolling6MonthAverage&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Month Name]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Month Name]&lt;/span&gt;&lt;span style="color:gray;"&gt;) 
  = &lt;/span&gt;&lt;span style="color:darkred;"&gt;Avg&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Month Name]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:blue;"&gt;Lag&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:magenta;"&gt;5&lt;/span&gt;&lt;span style="color:gray;"&gt;)&lt;/span&gt;&lt;span style="color:black;"&gt;:[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Month Name]&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:black;"&gt;[Measures]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Internet Sales Amount]&lt;/span&gt;&lt;span style="color:gray;"&gt;)&lt;/span&gt;&lt;span style="color:black;"&gt;;
  
&lt;/span&gt;&lt;span style="color:green;"&gt;// calcualte the growth of that 
&lt;/span&gt;&lt;span style="color:blue;"&gt;CREATE &lt;/span&gt;&lt;span style="color:black;"&gt;GrowthOfRolling6MonthAverage &lt;/span&gt;&lt;span style="color:gray;"&gt;= &lt;/span&gt;&lt;span style="color:black;"&gt;Rolling6MonthAverage &lt;/span&gt;&lt;span style="color:gray;"&gt;- (&lt;/span&gt;&lt;span style="color:black;"&gt;Rolling6MonthAverage&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Month Name]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:darkred;"&gt;PrevMember&lt;/span&gt;&lt;span style="color:gray;"&gt;)&lt;/span&gt;&lt;span style="color:black;"&gt;;

&lt;/span&gt;&lt;span style="color:green;"&gt;// calculate growth
&lt;/span&gt;&lt;span style="color:blue;"&gt;CREATE &lt;/span&gt;&lt;span style="color:black;"&gt;GrowthOfSales &lt;/span&gt;&lt;span style="color:gray;"&gt;= &lt;/span&gt;&lt;span style="color:black;"&gt;[Measures]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Internet Sales Amount] &lt;/span&gt;&lt;span style="color:gray;"&gt;- (&lt;/span&gt;&lt;span style="color:black;"&gt;[Measures]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Internet Sales Amount]&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Month Name]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:darkred;"&gt;PrevMember&lt;/span&gt;&lt;span style="color:gray;"&gt;)&lt;/span&gt;&lt;span style="color:black;"&gt;;

&lt;/span&gt;&lt;span style="color:green;"&gt;// count each &amp;quot;growth&amp;quot; calc to the row average 
&lt;/span&gt;&lt;span style="color:blue;"&gt;CREATE &lt;/span&gt;&lt;span style="color:black;"&gt;HowManyTimeGrowthExceededRollingAverage &lt;/span&gt;&lt;span style="color:gray;"&gt;= 
  &lt;/span&gt;&lt;span style="color:blue;"&gt;Count&lt;/span&gt;&lt;span style="color:gray;"&gt;(
    &lt;/span&gt;&lt;span style="color:darkred;"&gt;Filter&lt;/span&gt;&lt;span style="color:gray;"&gt;((&lt;/span&gt;&lt;span style="color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Calendar Year]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;&amp;amp;[2003]&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt;&lt;span style="color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Month Name]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Month Name]&lt;/span&gt;&lt;span style="color:gray;"&gt;), &lt;/span&gt;&lt;span style="color:black;"&gt;GrowthOfSales &lt;/span&gt;&lt;span style="color:gray;"&gt;&amp;gt; &lt;/span&gt;&lt;span style="color:black;"&gt;GrowthOfRolling6MonthAverage&lt;/span&gt;&lt;span style="color:gray;"&gt;))&lt;/span&gt;&lt;span style="color:black;"&gt;;

&lt;/span&gt;&lt;span style="color:blue;"&gt;CREATE SET &lt;/span&gt;&lt;span style="color:black;"&gt;ProductsThatGrewFasterThanRollingAverageMore6OrMoreTimes &lt;/span&gt;&lt;span style="color:blue;"&gt;AS
 &lt;/span&gt;&lt;span style="color:darkred;"&gt;Generate&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Customer]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Customer Geography]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Country]&lt;/span&gt;&lt;span style="color:gray;"&gt;, 
    &lt;/span&gt;&lt;span style="color:darkred;"&gt;CrossJoin&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Customer]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Customer Geography]&lt;/span&gt;&lt;span style="color:gray;"&gt;,
      &lt;/span&gt;&lt;span style="color:darkred;"&gt;Filter&lt;/span&gt;&lt;span style="color:gray;"&gt;(
        &lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;, 
        &lt;/span&gt;&lt;span style="color:black;"&gt;HowManyTimeGrowthExceededRollingAverage &lt;/span&gt;&lt;span style="color:gray;"&gt;&amp;gt;= &lt;/span&gt;&lt;span style="color:magenta;"&gt;6
      &lt;/span&gt;&lt;span style="color:gray;"&gt;)
    )
  )&lt;/span&gt;&lt;span style="color:black;"&gt;;

&lt;/span&gt;&lt;span style="color:blue;"&gt;SELECT &lt;/span&gt;&lt;span style="color:black;"&gt;{} &lt;/span&gt;&lt;span style="color:blue;"&gt;ON &lt;/span&gt;&lt;span style="color:magenta;"&gt;0
&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:black;"&gt;ProductsThatGrewFasterThanRollingAverageMore6OrMoreTimes &lt;/span&gt;&lt;span style="color:blue;"&gt;ON &lt;/span&gt;&lt;span style="color:magenta;"&gt;1
&lt;/span&gt;&lt;span style="color:blue;"&gt;FROM &lt;/span&gt;&lt;span style="color:black;"&gt;[Adventure Works]

&lt;/span&gt;&lt;span style="color:green;"&gt;// create growth from start to end of period
&lt;/span&gt;&lt;span style="color:blue;"&gt;CREATE &lt;/span&gt;&lt;span style="color:black;"&gt;GrowthFromStartToEnd &lt;/span&gt;&lt;span style="color:gray;"&gt;=
  (&lt;/span&gt;&lt;span style="color:black;"&gt;[Measures]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Internet Sales Amount]&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:darkred;"&gt;ClosingPeriod&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Calendar]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Month]&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Calendar]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Calendar Year]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;&amp;amp;[2002]&lt;/span&gt;&lt;span style="color:gray;"&gt;))
- (&lt;/span&gt;&lt;span style="color:black;"&gt;[Measures]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Internet Sales Amount]&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:darkred;"&gt;OpeningPeriod&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Calendar]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Month]&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Calendar]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Calendar Year]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;&amp;amp;[2002]&lt;/span&gt;&lt;span style="color:gray;"&gt;))&lt;/span&gt;&lt;span style="color:black;"&gt;;

&lt;/span&gt;&lt;span style="color:blue;"&gt;CREATE SET &lt;/span&gt;&lt;span style="color:black;"&gt;ProductsThatGrewMoreThan25PctFromStartToEnd &lt;/span&gt;&lt;span style="color:blue;"&gt;AS
  &lt;/span&gt;&lt;span style="color:darkred;"&gt;Generate&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Customer]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Customer Geography]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Country]&lt;/span&gt;&lt;span style="color:gray;"&gt;, 
    &lt;/span&gt;&lt;span style="color:darkred;"&gt;CrossJoin&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Customer]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Customer Geography]&lt;/span&gt;&lt;span style="color:gray;"&gt;,
      &lt;/span&gt;&lt;span style="color:darkred;"&gt;Filter&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Product]&lt;/span&gt;&lt;span style="color:gray;"&gt;, 
        &lt;/span&gt;&lt;span style="color:black;"&gt;GrowthFromStartToEnd &lt;/span&gt;&lt;span style="color:gray;"&gt;&amp;gt; &lt;/span&gt;&lt;span style="color:magenta;"&gt;0.25&lt;/span&gt;&lt;span style="color:gray;"&gt;*(&lt;/span&gt;&lt;span style="color:black;"&gt;[Measures]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Internet Sales Amount]&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:darkred;"&gt;OpeningPeriod&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Calendar]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Month]&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:black;"&gt;[Date]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Calendar]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;[Calendar Year]&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:black;"&gt;&amp;amp;[2002]&lt;/span&gt;&lt;span style="color:gray;"&gt;))
      )
    )
  )&lt;/span&gt;&lt;span style="color:black;"&gt;;

&lt;/span&gt;&lt;span style="color:blue;"&gt;SELECT &lt;/span&gt;&lt;span style="color:black;"&gt;{} &lt;/span&gt;&lt;span style="color:blue;"&gt;ON &lt;/span&gt;&lt;span style="color:magenta;"&gt;0
&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:black;"&gt;ProductsThatGrewMoreThan25PctFromStartToEnd &lt;/span&gt;&lt;span style="color:blue;"&gt;ON &lt;/span&gt;&lt;span style="color:magenta;"&gt;1
&lt;/span&gt;&lt;span style="color:blue;"&gt;FROM &lt;/span&gt;&lt;span style="color:black;"&gt;[Adventure Works]

&lt;/span&gt;&lt;span style="color:blue;"&gt;SELECT &lt;/span&gt;&lt;span style="color:black;"&gt;{} &lt;/span&gt;&lt;span style="color:blue;"&gt;ON &lt;/span&gt;&lt;span style="color:magenta;"&gt;0
&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:darkred;"&gt;Intersect&lt;/span&gt;&lt;span style="color:gray;"&gt;(
    &lt;/span&gt;&lt;span style="color:black;"&gt;ProductsThatGrewFasterThanRollingAverageMore6OrMoreTimes&lt;/span&gt;&lt;span style="color:gray;"&gt;,
    &lt;/span&gt;&lt;span style="color:black;"&gt;ProductsThatGrewMoreThan25PctFromStartToEnd &lt;/span&gt;&lt;span style="color:gray;"&gt;) &lt;/span&gt;&lt;span style="color:blue;"&gt;ON &lt;/span&gt;&lt;span style="color:magenta;"&gt;1
&lt;/span&gt;&lt;span style="color:blue;"&gt;FROM &lt;/span&gt;&lt;span style="color:black;"&gt;[Adventure Works]&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;&lt;a href="http://miky-schreiber.com/Blog/"&gt;Miky Schreiber&lt;/a&gt; said: 

      &lt;br /&gt;I agree with Thomas. The question is how difficult it is to the end user. While the regular end user will never write MDX or NextAnalytics script, the advanced user will. Now, the question is what will be more simple for him - MDX or NextAnalytics script.&lt;/em&gt;&amp;#160;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Valid point. But I don’t think there is an absolute measure of “what is simpler” – practitioners will tend to use the tool that they are more familiar with. MDX is probably more popular than nextanalytics, but SQL is way more popular than either one of these. And Excel will easily rival SQL in familiarity. On the other hand, developers will want to use “real” programming language. And the question of “what is simpler” will raise there as well. While functional languages like F# are clearly more suited for such tasks, more people are familiar with conventional languages like C#.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update: &lt;/strong&gt;Ward published the entire nextanalytics script for this problem, so constrast and compare it yourself: &lt;a href="http://www.nextanalytics.com/public/Examples/MDX-Challenge.html"&gt;http://www.nextanalytics.com/public/Examples/MDX-Challenge.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And finally Ward wrote long comment, from which I will cite selectively&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;&lt;a href="http://nextanalytics.com/"&gt;Ward Yaternick&lt;/a&gt; said:&lt;/em&gt;&amp;#160;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;I also don’t agree with your implicit assumption that the cube and other infrastructure parts are insignificant.&amp;#160;&amp;#160; To put a cube together from scratch and maintain it is a big decision and cost. &lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;…&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;Therefore, I think you have conclusively proved that (1) if the cube exists and (2) someone can author MDX then MDX does deliver on complex analytics.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I do agree with that. I wasn’t trying to say that setting up a cube was trivial. However, if the cube was set up for some particular purpose, it is very easy to extend it with MDX calculations to get additional value out of it.&lt;/p&gt;

&lt;blockquote&gt;&lt;em&gt;nextanalytics can help in these situations because the data can come directly from SQL or CSV files and the incremental IT overhead behind an analytic is very low.&amp;#160; You can feasibly write an analytic like what you did with MDX, run it against a CSV file or a simple SQL query, and then throw it away or keep it (all contained as lines in single text file) in a folder somewhere.&lt;/em&gt;&lt;/blockquote&gt;

&lt;p&gt;This certainly looks like a more agile way of doing things – quickly throw small script together, run it and move on. I think this approach will be attractive to lots of users – from small IT shops to independent users in bigger organizations. This is at odds, however, with classic methodology of building centralized data warehouse, one version of truth and all that. It doesn’t make it less viable, of course, it is just a tradeoff that someone will have to make – tradeoff between chaos and control, or, in Eric Raymond’s words, between bazaar and cathedral.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Another situation in which a cube isn’t ideal are when ISVs and consultants want to integrate analytics into their applications but they need to minimize the number of other vendor’s licenses and reduce the complexity and cost of the installed products.&amp;#160;&amp;#160; This priority is the same when an IT budget is under constraint.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think this is somewhat weak argument, because it implies that both complexity and pricing of NextAnalytics is below this of other products, like Microsoft Analysis Services. But this is definitely something that practitioners will need to evaluate and compare.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Sometimes, the analytic data processing needs to run on a unix box against something like MySQL or Oracle i.e. where Microsoft OLAP isn’t available&lt;/em&gt;&amp;#160;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Just wanted to note, that even though data could be in Unix box, in Oracle or MySQL, Microsoft OLAP doesn’t have problem connecting to it. Oracle is fully supported data source, and many people were successful reading data from MySQL too.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Perhaps they need an Open Source client&amp;#160; to make integration easier with their dashboard or portal.&amp;#160; For this reason, they can’t use any of the popular OLAP Clients&lt;/em&gt;&amp;#160;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I didn’t quite get this. There are plenty of Open Source clients which connect just fine to Microsoft Analysis Services (usually through XMLA, but not only). Since you mentioned Pentaho earlier, I wanted to note that Pentaho Analysis Views are based on JPivot, which works fine with Microsoft OLAP. There are many other open source clients for Microsoft OLAP as well. &lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;All this lands us where nextanalytics services it’s niches.&amp;#160; By now I hope you can see our positioning. We’re not competing with MDX especially in the conditions you wrote about.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;Yes, I think I see the positioning, and I believe that both your product and your approach have good potential, regardless of what MDX can and cannot do. And I actually think that it does compete, not just with MDX, but with OLAP and with Data Warehousing in general. And this is a good thing. Competition is healthy, it will force everybody to improve their products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update: &lt;/strong&gt;Nextanalytics replied at &lt;a href="http://www.nextanalytics.com/public/Examples/MDX-Challenge.html"&gt;http://www.nextanalytics.com/public/Examples/MDX-Challenge.html&lt;/a&gt;&lt;/p&gt;
&lt;BR&gt;&lt;div class = "shareblock"&gt;&lt;span class = "shareblockTitle"&gt;Share this post:&lt;/span&gt;&lt;span class = "shareblockLink"&gt; &lt;a href = "mailto:?subject=Follow up on NextAnalytics challenge&amp;amp;body=Seen on SQLblog.com: %0A%0A%09Follow up on NextAnalytics challenge%0A%0Ahttp://sqlblog.com/blogs/mosha/archive/2008/06/10/follow-up-on-nextanalytics-challenge.aspx" target="_blank" title = "Email Follow up on NextAnalytics challenge"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://sqlblog.com/blogs/mosha/archive/2008/06/10/follow-up-on-nextanalytics-challenge.aspx&amp;amp;title=Follow+up+on+NextAnalytics+challenge" target="_blank" title = "Submit Follow up on NextAnalytics challenge to del.icio.us"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/06/10/follow-up-on-nextanalytics-challenge.aspx&amp;amp;phase=2" target="_blank" title = "Submit Follow up on NextAnalytics challenge to digg.com"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/06/10/follow-up-on-nextanalytics-challenge.aspx&amp;amp;title=Follow+up+on+NextAnalytics+challenge" target="_blank" title = "Submit Follow up on NextAnalytics challenge to reddit.com"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://sqlblog.com/blogs/mosha/archive/2008/06/10/follow-up-on-nextanalytics-challenge.aspx&amp;amp;title=Follow+up+on+NextAnalytics+challenge" target="_blank" title = "Submit Follow up on NextAnalytics challenge to DotNetKicks"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;mkt=en-us&amp;amp;url=http://sqlblog.com/blogs/mosha/archive/2008/06/10/follow-up-on-nextanalytics-challenge.aspx&amp;amp;title=Follow+up+on+NextAnalytics+challenge&amp;amp;;top=1" target="_blank" title = "Add Follow up on NextAnalytics challenge to Live Bookmarks"&gt;live it!&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;img src="http://info.sqlblog.com/a.aspx?ZoneID=0&amp;BannerID=12&amp;AdvertiserID=1&amp;CampaignID=12&amp;Task=Get&amp;Mode=TEXT&amp;SiteID=1&amp;RandomNumber=463323" width="1" height="1" border="0"&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=7241" width="1" height="1"&gt;</content><author><name>mosha</name><uri>http://sqlblog.com/members/mosha.aspx</uri></author></entry><entry><title>MDX answer to NextAnalytics challenge</title><link rel="alternate" type="text/html" href="http://sqlblog.com/blogs/mosha/archive/2008/06/08/mdx-answer-to-nextanalytics-challenge.aspx" /><id>http://sqlblog.com/blogs/mosha/archive/2008/06/08/mdx-answer-to-nextanalytics-challenge.aspx</id><published>2008-06-09T00:05:00Z</published><updated>2008-06-09T00:05:00Z</updated><content type="html">&lt;P&gt;&lt;A href="http://www.nextanalytics.com/"&gt;NextAnalytics&lt;/A&gt; is a young BI company founded by Ward Yaternick of OLAP@Work fame. He has a blog dedicated to his new product, and while I was reading it, I ran across the post named “&lt;A href="http://www.nextanalytics.com/MyBlog/MyBlog/Can-a-business-intelligence-product-be-used-to-answer-analytic-questions.html"&gt;Can a business intelligence product be used to answer analytic questions?&lt;/A&gt;”. The main premises of this post was that real-life non-trivial calculations are very difficult with traditional BI tools using SQL or MDX, but can be done in much simpler fashion using NextAnalytics product. Ward then gives very concrete example:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;EM&gt;That’s the crux of the problem:&amp;nbsp; The nature of analytics often means that the need evolves.&amp;nbsp; It’s either because the analytics identified the problem or the analytic led to other questions.&amp;nbsp; By their nature, analytics are iterative and sequential. For example: &lt;/EM&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;
&lt;P&gt;&lt;EM&gt;I want to see the fastest growing products but it's more complicated than just telling me one measurement. &lt;/EM&gt;&lt;/P&gt;&lt;/LI&gt;
&lt;LI&gt;
&lt;P&gt;&lt;EM&gt;for each region, I want to see which products were growing faster than the rolling six month average, at least six times out of the last N periods. &lt;/EM&gt;&lt;/P&gt;&lt;/LI&gt;
&lt;LI&gt;
&lt;P&gt;&lt;EM&gt;next, again for each region, of those products that were just discovered, which ones grew by more than twenty five percent from the start of the period to the end of the period. &lt;/EM&gt;&lt;/P&gt;&lt;/LI&gt;
&lt;LI&gt;
&lt;P&gt;&lt;EM&gt;I want to know the intersection of those two sets of products across all regions.&amp;nbsp; &lt;/EM&gt;&lt;/P&gt;&lt;/LI&gt;
&lt;LI&gt;
&lt;P&gt;&lt;EM&gt;By the way, I won’t need these questions answered until next year at this time, although I might change them a bit. &lt;/EM&gt;&lt;/P&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Certainly this looks like something that can be done with basic MDX. Ward, however, stated that&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;EM&gt;That’s an example of one analytic combined with another, iteratively and sequentially. Neither of which would be possible in MDX or SQL without the creation of a complex underlying data structure.&amp;nbsp; Notice that this isn’t simply an OLAP&amp;nbsp; “drill down” or a “slice and dice”. These are distinct questions, with the answers needing to be merged and intersected.&amp;nbsp; This kind of query would take only a few hours in nextanalytics, or a few months in a BI tool.&lt;/EM&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;I left a comment to the blog saying that I disagreed with Ward’s statement, and unless by “complex underlying data structure” he meant regular OLAP cube, I thought this example can be easily done in MDX. Ward took his time to reply to my comment, going into detail how this would be done with nextanalytics:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;EM&gt;Allow me to describe the processing a bit better in a pseudocode style: &lt;BR&gt;// the column axis has time periods, the row axis has regions and products. &lt;BR&gt;for each region &lt;BR&gt;{ &lt;BR&gt;// part 1 &lt;BR&gt;calculate the rolling six month average &lt;BR&gt;calcualte the growth of that &lt;BR&gt;count each "growth" calc to the row average &lt;BR&gt;keep only rows above average six or more times &lt;BR&gt;// part 2 &lt;BR&gt;remove all time periods except the first and last &lt;BR&gt;calculate growth &lt;BR&gt;remove any below 25 % &lt;BR&gt;// part 3 &lt;BR&gt;intersect part 1 and part 2 &lt;BR&gt;} &lt;/EM&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;And then he threw a gauntlet, challenging me to do the same in MDX as easily as the pseudocode above.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;BR&gt;&lt;EM&gt;Note that in part 1, the output of the rolling average becomes the input to the growth calculations. &lt;BR&gt;Now that I've been more clear, do you still think this is achievable in MDX? Is it fair of me to qualify your claim by saying "In a single MDX expression?" "someone with a normal amount of MDX education" and "not having to create intermediate data structures in a server or disk"? Because, if it takes any of that, then my blog's point still stands IMO. &lt;BR&gt;To do all that with nextanalytics is about just as many lines as what I just typed above. No external tables, no external cubes. And, the nextanalytics "code" is still readable a year later and easily modified. &lt;/EM&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Well, I decided to pick up this challenge, and write complete implementation of this problem in MDX using Adventure Works sample database. I also decided to time myself to see how much time it will actually take – to have real answer to the claim that it would take “&lt;EM&gt;few months in a BI tool&lt;/EM&gt;”. This weekend it was mix of rain and clouds in Seattle, so my family didn’t go backpacking, and I thought that I would use 2 hour window between teaching my son to bike on two wheels and going to watch “&lt;A href="http://www.imdb.com/title/tt0499448/"&gt;Chronicles of Narnia: Prince Caspian&lt;/A&gt;”, to implement this. This blog presents the results of this experiment.&lt;/P&gt;
&lt;P&gt;I used &lt;A class="" href="http://www.mosha.com/msolap/mdxstudio.htm"&gt;MDX Studio&lt;/A&gt; to develop all of the code. One nice thing about it, is that it allows mixing MDX Script type of statement, which I used to define calculated measures with SELECT statements. In order to do it, I just specified “Cube=Adventure Works” in the connection string, and everything worked fine. (Of course, I also could’ve used more traditional syntax of CREATE MEMBER statement, but I am too used to MDX Scripts now). &lt;/P&gt;
&lt;P&gt;I went line by line over the Ward’s pseudocode writing MDX for it. I didn’t try to write the super-optimized MDX, and I didn’t do any smart tricks – I just wanted to write natural, most straightforward MDX, that average MDX developer would’ve done in my place.&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;calculate the rolling six month average &lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;There are several techniques how to approach it, and I wrote both chapter in my “Fast Track to MDX” book and blog entry about it, but let’s take the simplest approach:&lt;/P&gt;&lt;PRE&gt;// calculate the rolling six month average 
CREATE Rolling6MonthAverage;
(Rolling6MonthAverage, [Date].[Month Name].[Month Name]) 
  = Avg([Date].[Month Name].Lag(5):[Date].[Month Name], [Measures].[Internet Sales Amount]);&lt;/PRE&gt;
&lt;P&gt;This leaves the calculated measure to be NULL at the attributes above (or unrelated to) Month, and computes rolling 6 month average for Month and all attributes to which Month is related (i.e. Day etc).&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;calcualte the growth of that &lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;That’s the part where Ward made a comment of “&lt;EM&gt;Note that in part 1, the output of the rolling average becomes the input to the growth calculations&lt;/EM&gt;”. I just wanted to note, that this is absolutely routine in MDX&lt;/P&gt;&lt;PRE&gt;// calcualte the growth of that 
CREATE GrowthOfRolling6MonthAverage = Rolling6MonthAverage - (Rolling6MonthAverage, [Date].[Month Name].PrevMember);&lt;/PRE&gt;
&lt;P&gt;&lt;EM&gt;count each "growth" calc to the row average &lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;The way I interpret this is we need to compare sales growth to the growth of rolling average and then count how many times sales growth exceeded growth of rolling average. For simplicity of reading, I broke this into two calculated measures, one computing sales growth, and another one doing counting. Of course, it could’ve been done in one calculated measure too, but this way it is a little more natural to read.&lt;/P&gt;&lt;PRE&gt;// calculate growth
CREATE GrowthOfSales = [Measures].[Internet Sales Amount] - ([Measures].[Internet Sales Amount], [Date].[Month Name].PrevMember);&lt;/PRE&gt;
&lt;P&gt;In the original posting, Ward said “for each region, I want to see which products were growing faster than the rolling six month average, at least six times out of the last N periods.” So I really had choose what N periods would be in my example – and I choose that we are interested in the months of 2003.&lt;/P&gt;&lt;PRE&gt;// count each "growth" calc to the row average 
CREATE HowManyTimeGrowthExceededRollingAverage = 
  Count(
    Filter(([Date].[Calendar Year].&amp;amp;[2003],[Date].[Month Name].[Month Name]), GrowthOfSales &amp;gt; GrowthOfRolling6MonthAverage));&lt;/PRE&gt;
&lt;P&gt;&lt;EM&gt;keep only rows above average six or more times &lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;This is simple filter, but since we need to find products for every region, we also need to run Generate over regions. For Adventure Works, I choose Country attribute to represent regions:&lt;/P&gt;&lt;PRE&gt;// keep only rows above average six or more times 
CREATE SET ProductsThatGrewFasterThanRollingAverageMore6OrMoreTimes AS
 Generate([Customer].[Customer Geography].[Country], 
    CrossJoin([Customer].[Customer Geography],
      Filter(
        [Product].[Product].[Product], 
        HowManyTimeGrowthExceededRollingAverage &amp;gt;= 6
      )
    )
  );&lt;/PRE&gt;
&lt;P&gt;My understanding of nextanalytics scripting language is that it computes static sets in certain context, and this is why I used CREATE SET statement in MDX. However, MDX is obviously capable of computing such a set completely dynamically, depending on the current context, slices, subcubes etc.&lt;/P&gt;&lt;EM&gt;remove all time periods except the first and last &lt;BR&gt;calculate growth &lt;/EM&gt;
&lt;P&gt;Aha, here we have an opportunity to replace two lines of nextanalytics script with just one line of MDX. We don’t need to remove anything to calculate growth from the start of period to its end. Assuming again that the period is months in 2003, we would just use OpeningPeriod and ClosingPeriod MDX functions.&lt;/P&gt;&lt;PRE&gt;// create growth from start to end of period
CREATE GrowthFromStartToEnd =
  ([Measures].[Internet Sales Amount], ClosingPeriod([Date].[Calendar].[Month], [Date].[Calendar].[Calendar Year].&amp;amp;[2002]))
- ([Measures].[Internet Sales Amount], OpeningPeriod([Date].[Calendar].[Month], [Date].[Calendar].[Calendar Year].&amp;amp;[2002]));&lt;/PRE&gt;
&lt;P&gt;&lt;EM&gt;remove any below 25 % &lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;One way to do it is to create calculated measure which computes relative growth as percentage, but then we would need to deal with division by zero handling, so it is simpler to code the 25% rule inside the Filter itself.&lt;/P&gt;&lt;PRE&gt;CREATE SET ProductsThatGrewMoreThan25PctFromStartToEnd AS
  Generate([Customer].[Customer Geography].[Country], 
    CrossJoin([Customer].[Customer Geography],
      Filter([Product].[Product].[Product], 
        GrowthFromStartToEnd &amp;gt; 
           0.25*([Measures].[Internet Sales Amount], 
             OpeningPeriod([Date].[Calendar].[Month], [Date].[Calendar].[Calendar Year].&amp;amp;[2002]))
      )
    )
  );&lt;/PRE&gt;
&lt;P&gt;&lt;EM&gt;intersect part 1 and part 2&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;Just to prove the point, let’s use Intersect function. However, better approach would’ve been to use the Generate(Filter()), and combine the conditions inside Filter using AND. The following query shows final set of products by regions:&lt;/P&gt;&lt;PRE&gt;SELECT {} ON 0
, Intersect(
    ProductsThatGrewFasterThanRollingAverageMore6OrMoreTimes,
    ProductsThatGrewMoreThan25PctFromStartToEnd ) ON 1
FROM [Adventure Works]&lt;/PRE&gt;
&lt;P&gt;Hopefully this exercise will convince Ward that MDX is pretty powerful, yet simple language which allows for complex analysis. But how long did it take to implement all this logic. I measured myself, and it only took 21 minutes of my time to write all of the MDX statements. However, this also included about 4 minutes to arbitrage dispute between my kids, so net spent time was 17 minutes. Factoring that I am probably faster at writing MDX than average, I estimate that it would take around 1 hour to somebody with “&lt;EM&gt;normal amount of MDX education”&lt;/EM&gt;. Much better than “&lt;EM&gt;few months in BI tool”&lt;/EM&gt; and also somewhat better than “&lt;EM&gt;few hours in nextanalytics”.&lt;/EM&gt;&lt;/P&gt;
&lt;BR&gt;&lt;div class = "shareblock"&gt;&lt;span class = "shareblockTitle"&gt;Share this post:&lt;/span&gt;&lt;span class = "shareblockLink"&gt; &lt;a href = "mailto:?subject=MDX answer to NextAnalytics challenge&amp;amp;body=Seen on SQLblog.com: %0A%0A%09MDX answer to NextAnalytics challenge%0A%0Ahttp://sqlblog.com/blogs/mosha/archive/2008/06/08/mdx-answer-to-nextanalytics-challenge.aspx" target="_blank" title = "Email MDX answer to NextAnalytics challenge"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://sqlblog.com/blogs/mosha/archive/2008/06/08/mdx-answer-to-nextanalytics-challenge.aspx&amp;amp;title=MDX+answer+to+NextAnalytics+challenge" target="_blank" title = "Submit MDX answer to NextAnalytics challenge to del.icio.us"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/06/08/mdx-answer-to-nextanalytics-challenge.aspx&amp;amp;phase=2" target="_blank" title = "Submit MDX answer to NextAnalytics challenge to digg.com"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/06/08/mdx-answer-to-nextanalytics-challenge.aspx&amp;amp;title=MDX+answer+to+NextAnalytics+challenge" target="_blank" title = "Submit MDX answer to NextAnalytics challenge to reddit.com"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://sqlblog.com/blogs/mosha/archive/2008/06/08/mdx-answer-to-nextanalytics-challenge.aspx&amp;amp;title=MDX+answer+to+NextAnalytics+challenge" target="_blank" title = "Submit MDX answer to NextAnalytics challenge to DotNetKicks"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;mkt=en-us&amp;amp;url=http://sqlblog.com/blogs/mosha/archive/2008/06/08/mdx-answer-to-nextanalytics-challenge.aspx&amp;amp;title=MDX+answer+to+NextAnalytics+challenge&amp;amp;;top=1" target="_blank" title = "Add MDX answer to NextAnalytics challenge to Live Bookmarks"&gt;live it!&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;img src="http://info.sqlblog.com/a.aspx?ZoneID=0&amp;BannerID=12&amp;AdvertiserID=1&amp;CampaignID=12&amp;Task=Get&amp;Mode=TEXT&amp;SiteID=1&amp;RandomNumber=463323" width="1" height="1" border="0"&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=7196" width="1" height="1"&gt;</content><author><name>mosha</name><uri>http://sqlblog.com/members/mosha.aspx</uri></author><category term="mdx" scheme="http://sqlblog.com/blogs/mosha/archive/tags/mdx/default.aspx" /></entry><entry><title>Take advantage of FE caching to optimize MDX performance</title><link rel="alternate" type="text/html" href="http://sqlblog.com/blogs/mosha/archive/2008/03/28/take-advantage-of-fe-caching-to-optimize-mdx-performance.aspx" /><id>http://sqlblog.com/blogs/mosha/archive/2008/03/28/take-advantage-of-fe-caching-to-optimize-mdx-performance.aspx</id><published>2008-03-28T23:46:01Z</published><updated>2008-03-28T23:46:01Z</updated><content type="html">&lt;p&gt;We usually&amp;nbsp;treat caching system of Analysis Services as a black box, trusting that it will do the "right thing" to optimize the execution. And this is how things should be in the ideal world, caching system should&amp;nbsp;be completely transparent to the end user. However, even though MDX query optimizer and caching system are very sophisticated, they are not perfect. In this article we will see how with very simple MDX rewrites, we can take better advantage of caching, and increase performance of MDX calculations significantly.&lt;/p&gt; &lt;p&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/commons/thumb/8/8c/Standard_deviation_diagram.svg/350px-Standard_deviation_diagram.svg.png" align="right"&gt;(In order to follow examples in this article, you will need to use &lt;a&gt;MDX Studio&lt;/a&gt; tool)&lt;/p&gt; &lt;p&gt;As an example we will use very simple statistical analysis of the data, where we would apply "&lt;a href="http://en.wikipedia.org/wiki/68-95-99.7_rule"&gt;Three sigma rule&lt;/a&gt;" to find outliers across combination of attributes. It is based on the fact that for data which conforms &lt;a href="http://en.wikipedia.org/wiki/Normal_distribution"&gt;normal distribution&lt;/a&gt;&amp;nbsp;almost all of the values (99.7% of them) will lay within 3 standard deviations of the mean. So the values which are farther from the mean by more than 3 standard deviations can be considered outliers. (There are of course more&amp;nbsp;advanced methods to find outliers in the data, but for the purpose of this article this is representative enough). &lt;/p&gt; &lt;p&gt;We will start with the analysis of Internet Sales by day inside Adventure Works cube. The MDX to find out "farther than three sigma" days seems straightforward&lt;/p&gt;&lt;pre&gt;with 
 member sales_avg   as Avg  ([Date].[Date].[Date], [Internet Sales Amount]), format_string = 'currency'
 member sales_stdev as StDev([Date].[Date].[Date], [Internet Sales Amount]), format_string = 'currency'
select { [Internet Sales Amount] } on 0
, filter([Date].[Date].[Date], abs([Internet Sales Amount]-sales_avg) &amp;gt; 3*sales_stdev) on 1
from [Adventure Works]
&lt;/pre&gt;
&lt;p&gt;If we execute this query we will get the list of 10 outlier dates. But the query takes 7.5 seconds to execute. This is horrible !&amp;nbsp;7.5 seconds to go through 1158 members seem completely unreasonable. To get a better idea about what's going on, let's take a look at some execution statistics collected by MDX Studio:&lt;/p&gt;&lt;pre&gt;Time             : 7 sec 417 ms
Calc covers      : 1164
Cells calculated : 1344448
Sonar subcubes   : 3
SE queries       : 1159
Cache hits       : 1159
Cache misses     : 2
Cache inserts    : 2
Cache lookups    : 1161
&lt;/pre&gt;
&lt;p&gt;Now we can understand what was going on here. For each one of these 1158 members, the engine recalculated both average and standard deviation, even though they were exactly the same at each point (it is easy to verify through debugger in MDX Studio). The problem is that&amp;nbsp;the engine&amp;nbsp;didn't figure out itself that average and standard deviation were the same for all dates, so we need to help it a little bit. We can redefine&amp;nbsp;sales_avg as a calculation which goes up to All member in Date attribute. This way regardless at which date we are looking right now, the cell coordinate will shift to the All member, and will be computed only once, because all the other times it is referenced it can be answered from FE cache. Calculation rewritten with this trick in mind will look the following:&lt;/p&gt;&lt;pre&gt;with 
 member sales_avg_   as Avg  ([Date].[Date].[Date], [Internet Sales Amount]), format_string = 'currency'
 member sales_stdev_ as StDev([Date].[Date].[Date], [Internet Sales Amount]), format_string = 'currency'
 member sales_avg as ([Date].[Date].[All Periods],sales_avg_), format_string = 'currency'
 member sales_stdev as ([Date].[Date].[All Periods],sales_stdev_), format_string = 'currency'
select { [Internet Sales Amount] } on 0
, filter([Date].[Date].[Date], abs([Internet Sales Amount]-sales_avg) &amp;gt; 3*sales_stdev) on 1
from [Adventure Works]
&lt;/pre&gt;
&lt;p&gt;If we execute it now, it is done in mere 0.1 fraction of second. The MDX Studio stats look like following&lt;/p&gt;&lt;pre&gt;Time             : 109 ms
Calc covers      : 9
Cells calculated : 4642
Sonar subcubes   : 3
SE queries       : 2
Cache hits       : 4
Cache misses     : 2
Cache inserts    : 2
Cache lookups    : 6
&lt;/pre&gt;
&lt;p&gt;This is much better. Instead of SE query per cell, we got only 2 SE queries (one for axis and one for avg/stdev calculation). There are also significantly less cells calculated, since both average and standard deviation are calculated only once now and served from the cache.&lt;/p&gt;
&lt;p&gt;Let's make the problem a little bit complex now. We computed statistics across all 4 years, and discovered that all the outlier dates were in 2004. This is probably attributed to the growth over time. So let's compute statistics differently now, instead of computing them for all dates in 4 years, let's compute them within each month, and find outliers within the month. The straightforward approach to this can be coded in MDX as following.&lt;/p&gt;&lt;pre&gt;with 
 member sales_avg   as Avg  ([Date].[Calendar].Parent.children, [Internet Sales Amount]), format_string = 'currency'
 member sales_stdev as StDev([Date].[Calendar].Parent.children, [Internet Sales Amount]), format_string = 'currency'
select { [Internet Sales Amount], sales_avg, sales_stdev } on 0
, filter([Date].[Date].[Date], abs([Internet Sales Amount]-sales_avg) &amp;gt; 3*sales_stdev) on 1
from [Adventure Works]
&lt;/pre&gt;
&lt;p&gt;Executing this query now finds two outliers:&lt;/p&gt;
&lt;table cellspacing="0" cellpadding="2"&gt;

&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Internet Sales Amount&amp;nbsp;&lt;/td&gt;
&lt;td&gt;sales_avg&amp;nbsp;&lt;/td&gt;
&lt;td&gt;sales_stdev&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;July 22, 2001&lt;/td&gt;
&lt;td&gt;$38,241.29&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/td&gt;
&lt;td&gt;$15,270.59&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/td&gt;
&lt;td&gt;$6,747.86&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;December 8, 2001&lt;/td&gt;
&lt;td&gt;$55,454.04&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/td&gt;
&lt;td&gt;$24,371.87&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;$10,146.40&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;And the MDX Studio stats will be&lt;/p&gt;&lt;pre&gt;Time             : 704 ms
Calc covers      : 1206
Cells calculated : 38792
Sonar subcubes   : 41
SE queries       : 1198
Cache hits       : 1198
Cache misses     : 2
Cache inserts    : 2
Cache lookups    : 1200
&lt;/pre&gt;
&lt;p&gt;Again, the stats don't look good. Too many SE queries, too many cells calculated. How can we apply the same technique as before ? Moving coordinate to the All dates will be incorrect now, because average and standard deviations are now different for every month. Therefore we need to move coordinates to the month level. The most natural way of doing it is simple move call to the Parent function from within the Avg/Stdev calls into the tuple coordinate shift transformation. Here is how the resulting query will look like:&lt;/p&gt;&lt;pre&gt;with 
 member sales_avg_   as Avg  ([Date].[Calendar].children, [Internet Sales Amount]), format_string = 'currency'
 member sales_stdev_ as StDev([Date].[Calendar].children, [Internet Sales Amount]), format_string = 'currency'
 member sales_avg    as ([Date].[Calendar].Parent,sales_avg_), format_string = 'currency'
 member sales_stdev  as ([Date].[Calendar].Parent,sales_stdev_), format_string = 'currency'
select { [Internet Sales Amount], sales_avg, sales_stdev } on 0
, filter([Date].[Date].[Date], abs([Internet Sales Amount]-sales_avg) &amp;gt; 3*sales_stdev) on 1
from [Adventure Works]
&lt;/pre&gt;
&lt;p&gt;The stats from this rewrite will be&lt;/p&gt;&lt;pre&gt;Time             : 203 ms
Calc covers      : 162
Cells calculated : 6954
Sonar subcubes   : 117
SE queries       : 78
Cache hits       : 78
Cache misses     : 2
Cache inserts    : 2
Cache lookups    : 80
&lt;/pre&gt;
&lt;p&gt;Which is much more reasonable result.&lt;/p&gt;
&lt;BR&gt;&lt;div class = "shareblock"&gt;&lt;span class = "shareblockTitle"&gt;Share this post:&lt;/span&gt;&lt;span class = "shareblockLink"&gt; &lt;a href = "mailto:?subject=Take advantage of FE caching to optimize MDX performance&amp;amp;body=Seen on SQLblog.com: %0A%0A%09Take advantage of FE caching to optimize MDX performance%0A%0Ahttp://sqlblog.com/blogs/mosha/archive/2008/03/28/take-advantage-of-fe-caching-to-optimize-mdx-performance.aspx" target="_blank" title = "Email Take advantage of FE caching to optimize MDX performance"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://sqlblog.com/blogs/mosha/archive/2008/03/28/take-advantage-of-fe-caching-to-optimize-mdx-performance.aspx&amp;amp;title=Take+advantage+of+FE+caching+to+optimize+MDX+performance" target="_blank" title = "Submit Take advantage of FE caching to optimize MDX performance to del.icio.us"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/03/28/take-advantage-of-fe-caching-to-optimize-mdx-performance.aspx&amp;amp;phase=2" target="_blank" title = "Submit Take advantage of FE caching to optimize MDX performance to digg.com"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://sqlblog.com/blogs/mosha/archive/2008/03/28/take-advantage-of-fe-caching-to-optimize-mdx-performance.aspx&amp;amp;title=Take+advantage+of+FE+caching+to+optimize+MDX+performance" target="_blank" title = "Submit Take advantage of FE caching to optimize MDX performance to reddit.com"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://sqlblog.com/blogs/mosha/archive/2008/03/28/take-advantage-of-fe-caching-to-optimize-mdx-performance.aspx&amp;amp;title=Take+advantage+of+FE+caching+to+optimize+MDX+performance" target="_blank" title = "Submit Take advantage of FE caching to optimize MDX performance to DotNetKicks"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;mkt=en-us&amp;amp;url=http://sqlblog.com/blogs/mosha/archive/2008/03/28/take-advantage-of-fe-caching-to-optimize-mdx-performance.aspx&amp;amp;title=Take+advantage+of+FE+caching+to+optimize+MDX+performance&amp;amp;;top=1" target="_blank" title = "Add Take advantage of FE caching to optimize MDX performance to Live Bookmarks"&gt;live it!&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;img src="http://info.sqlblog.com/a.aspx?ZoneID=0&amp;BannerID=12&amp;AdvertiserID=1&amp;CampaignID=12&amp;Task=Get&amp;Mode=TEXT&amp;SiteID=1&amp;RandomNumber=463323" width="1" height="1" border="0"&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=5881" width="1" height="1"&gt;</content><author><name>mosha</name><uri>http://sqlblog.com/members/mosha.aspx</uri></author></entry><entry><title>MDX and partitioning</title><link rel="alternate" type="text/html" href="http://sqlblog.com/blogs/mosha/archive/2007/12/18/mdx-and-partitioning.aspx" /><id>http://sqlblog.com/blogs/mosha/archive/2007/12/18/mdx-and-partitioning.aspx</id><published>2007-12-19T04:21:00Z</published><updated>2007-12-19T04:21:00Z</updated><content type="html">&lt;P&gt;Partitions have been a built-in feature of Analysis Services ever since the first version. Naturally, all the engine subsystems, including the MDX query optimizer,&amp;nbsp;work well with partitions. There are rare cases, however, when over-aggressive prefetching can generate query plan which is not optimal with respect to the partitioning scheme. Today we will review one such scenario and see how MDX could be rewritten to suit the partitioning scheme better.&lt;/P&gt;
&lt;P&gt;Let's consider scenario where we need to determine the last date for which there is data in certain measure group. The example will be built around Adventure Works sample database, using Date dimension and Internet Sales Amount as a measure. &lt;/P&gt;
&lt;P&gt;&lt;EM&gt;One more thing to note: while writing this post I used MDX Studio which simplifies many operations, so when below I say "look at the set", it means inspecting it in the Watch window of MDX Studio, when I say "clear the cache", it means using MDX Studio's cache clear button etc. The only other tool that needs to be run is Profiler, since AS traces are not yet integrated into MDX Studio, but the MDX Studio development team is working on this feature. Also, in order to be able to run MDX Script statements from within MDX Studio, I added "Cube=Adventure Works" in the Connection Properties field of the Connect dialog.&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;EM&gt;Due to limitations of sqlblog.com, in all examples below remove the space before the square braket in the&amp;nbsp;[Date ] fragments&lt;/EM&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The most straightforward way to compute the last date is by using the following formula:&lt;/P&gt;&lt;PRE&gt;CREATE SET CurrentCube.LastDate as Tail(NonEmpty([Date ].[Date ].[Date ], [Measures].[Internet Sales Amount]), 1)
&lt;/PRE&gt;
&lt;P&gt;We can see that the result set will have a single member in it - July 31, 2004. But if we execute this statement over the clean cache, we can see the following events in the trace:&lt;/P&gt;&lt;PRE&gt;Query Begin&amp;nbsp;&amp;nbsp;&amp;nbsp; 0 - MDXQuery&amp;nbsp;&amp;nbsp;&amp;nbsp; MDX Studio v0.2.6.0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;Progress Report Begin&amp;nbsp;&amp;nbsp;&amp;nbsp; 14 - Query&amp;nbsp;&amp;nbsp;&amp;nbsp; Started reading data from the 'Internet_Sales_2001' partition.&amp;nbsp;&amp;nbsp;&lt;BR&gt;Progress Report Begin&amp;nbsp;&amp;nbsp;&amp;nbsp; 14 - Query&amp;nbsp;&amp;nbsp;&amp;nbsp; Started reading data from the 'Internet_Sales_2002' partition.&amp;nbsp;&amp;nbsp;&lt;BR&gt;Progress Report Begin&amp;nbsp;&amp;nbsp;&amp;nbsp; 14 - Query&amp;nbsp;&amp;nbsp;&amp;nbsp; Started reading data from the 'Internet_Sales_2003' partition.&amp;nbsp;&amp;nbsp;&lt;BR&gt;Progress Report End&amp;nbsp;&amp;nbsp;&amp;nbsp; 14 - Query&amp;nbsp;&amp;nbsp;&amp;nbsp; Finished reading data from the 'Internet_Sales_2001' partition.&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;BR&gt;Progress Report Begin&amp;nbsp;&amp;nbsp;&amp;nbsp; 14 - Query&amp;nbsp;&amp;nbsp;&amp;nbsp; Started reading data from the 'Internet_Sales_2004' partition.&amp;nbsp;&amp;nbsp;&lt;BR&gt;Progress Report End&amp;nbsp;&amp;nbsp;&amp;nbsp; 14 - Query&amp;nbsp;&amp;nbsp;&amp;nbsp; Finished reading data from the 'Internet_Sales_2002' partition.&amp;nbsp;&amp;nbsp;&lt;BR&gt;Progress Report End&amp;nbsp;&amp;nbsp;&amp;nbsp; 14 - Query&amp;nbsp;&amp;nbsp;&amp;nbsp; Finished reading data from the 'Internet_Sales_2003' partition.&amp;nbsp;&amp;nbsp;&lt;BR&gt;Progress Report End&amp;nbsp;&amp;nbsp;&amp;nbsp; 14 - Query&amp;nbsp;&amp;nbsp;&amp;nbsp; Finished reading data from the 'Internet_Sales_2004' partition.&amp;nbsp;&amp;nbsp;&lt;BR&gt;Query End&amp;nbsp;&amp;nbsp;&amp;nbsp; 0 - MDXQuery&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/PRE&gt;
&lt;P&gt;We see that all partitions got queried, even though the last non empty date is in 2004, and therefore it should've been enough to query just the Internet_Sales_2004 partition. Unfortunately, the query optimizer gets carried away in this example, decides to ignore the outer Tail(..., 1) and executes NonEmpty over all dates literally over all dates. So, we need a different strategy here. The second obvious strategy for finding last non empty date is the recursive approach - start from the last date in the dimension and move back by one date until we run into a date which has data. The formula for that is &lt;/P&gt;&lt;PRE&gt;CREATE
 MEMBER CurrentCube.LastDateIndex AS
   Iif(
    IsEmpty([Measures].[Internet Sales Amount]), 
    [Date ].[Calendar].PrevMember,
    Rank([Date ].[Calendar].CurrentMember, [Date ].[Calendar].[Date ])
   )
 SET LastDate AS [Date ].[Calendar].[Date ].Item((LastDateIndex, Tail([Date ].[Calendar].[Date ],1).Item(0))-1)&lt;/PRE&gt;
&lt;P&gt;Here we implemented the recursive logic inside calculated member LastDateIndex, and then we position it on the last date (using Tail(1) over level of dates for that). The result is the same as before, but trace looks very different:&lt;/P&gt;&lt;PRE&gt;Query Begin	0 - MDXQuery	MDX Studio v0.2.6.0		
Query Subcube	2 - Non-cache data August 31, 2004	
Query Subcube	2 - Non-cache data August 30, 2004	
Query Subcube	2 - Non-cache data August 29, 2004	
Query Subcube	2 - Non-cache data August 28, 2004	
Query Subcube	2 - Non-cache data August 27, 2004	
Query Subcube	2 - Non-cache data August 26, 2004	
Query Subcube	2 - Non-cache data August 25, 2004	
Query Subcube	2 - Non-cache data August 24, 2004	
Query Subcube	2 - Non-cache data August 23, 2004	
Query Subcube	2 - Non-cache data August 22, 2004	
Query Subcube	2 - Non-cache data August 21, 2004	
Query Subcube	2 - Non-cache data August 20, 2004	
Query Subcube	2 - Non-cache data August 19, 2004	
Query Subcube	2 - Non-cache data August 18, 2004	
Query Subcube	2 - Non-cache data August 17, 2004	
Query Subcube	2 - Non-cache data August 16, 2004	
Query Subcube	2 - Non-cache data August 15, 2004	
Query Subcube	2 - Non-cache data August 14, 2004	
Query Subcube	2 - Non-cache data August 13, 2004	
Query Subcube	2 - Non-cache data August 12, 2004	
Query Subcube	2 - Non-cache data August 11, 2004	
Query Subcube	2 - Non-cache data August 10, 2004	
Query Subcube	2 - Non-cache data August 9, 2004	
Query Subcube	2 - Non-cache data August 8, 2004	
Query Subcube	2 - Non-cache data August 7, 2004	
Query Subcube	2 - Non-cache data August 6, 2004	
Query Subcube	2 - Non-cache data August 5, 2004	
Query Subcube	2 - Non-cache data August 4, 2004	
Query Subcube	2 - Non-cache data August 3, 2004	
Query Subcube	2 - Non-cache data August 2, 2004	
Query Subcube	2 - Non-cache data August 1, 2004	
Progress Report Begin	14 - Query	Started reading data from the 'Internet_Sales_2004' partition.
Progress Report End	14 - Query	Finished reading data from the 'Internet_Sales_2004' partition.
Query Subcube	2 - Non-cache data July 31, 2004	
Query End	0 - MDXQuery	
&lt;/PRE&gt;
&lt;P&gt;Now we see lots of non-cached Query Subcube&amp;nbsp;requests for the dates going from August 31, 2004 down to July 31, 2004. Interestingly, only the last one triggers reading of the partition, since for the others, partition autoslice automatically detects that they don't belong to any partition. So in certain sense this is a good result, since we only touched one partition, but in another sense, this query plan with lots of Query Subcube requests doesn't look good. We were lucky, that in Adventure Works there were only 31 days at the end of Date dimension without data, so recursion went 31 step only. But it is not uncommon to have&amp;nbsp;Date dimensions&amp;nbsp;looking several years ahead, in which case recursion could go on for thousands of steps, and overhead of thousands of Query Subcube requests could become substantial, not to mention the overhead of the depth&amp;nbsp;of recursion.&lt;/P&gt;
&lt;P&gt;This leads us to another approach. Instead of iterating by days, we can iterate by partitions. Since in Adventure Works partitioning scheme is by years, we will iterate by years, and then find the last non empty date within a year. The MDX that implements this approach is below:&lt;/P&gt;&lt;PRE&gt;CREATE
 MEMBER CurrentCube.LastYearIndex AS
  Iif(
   IsEmpty([Measures].[Internet Sales Amount]), 
   [Date ].[Calendar Year].PrevMember,
   Rank([Date ].[Calendar Year].CurrentMember, [Date ].[Calendar Year].[Calendar Year])
  )
 SET LastYear AS [Date ].[Calendar Year].[Calendar Year].Item((LastYearIndex, [Date ].[Calendar Year].LastChild)-1)
 SET LastDate AS Tail(NonEmpty([Date ].[Date ].[Date ].MEMBERS, (LastYear, [Measures].[Internet Sales Amount])), 1)
&lt;/PRE&gt;
&lt;P&gt;Again, this gives us the desired result, and now the trace looks like following:&lt;/P&gt;&lt;PRE&gt;Query Begin	0 - MDXQuery	MDX Studio v0.2.6.0
Progress Report Begin	14 - Query	Started reading data from the 'Internet_Sales_2004' partition.
Progress Report End	14 - Query	Finished reading data from the 'Internet_Sales_2004' partition.
Query Subcube	2 - Non-cache data
00000000,000,00000,00,000000000000100000,000000000000000000,000000000000000000,0000000000000000000000,000000000000000000000,00,10
Progress Report Begin	14 - Query	Started reading data from the 'Internet_Sales_2004' partition.
Progress Report End	14 - Query	Finished reading data from the 'Internet_Sales_2004' partition.
Query Subcube	2 - Non-cache data
00000000,000,00000,00,010000000000100000,000000000000000000,000000000000000000,0000000000000000000000,000000000000000000000,00,10
Query End	0 - MDXQuery
&lt;/PRE&gt;
&lt;P&gt;We eliminated lots of Query Subcube requests, but we still have two, and now each one triggers query of partition. Why there are two of them ? This is easily answered by inspecting the Query Subcube granularity bitmasks. We see that the difference between them is in the Date dimension. First Query Subcube doesn't have granularity on Date attribute, while the second one has. It is clear now, that the first Query Subcube request is triggered by IsEmpty([Measures].[Internet Sales Amount]) check inside Iif - because it happens at the Year granularity, and the second Query Subcube is due to NonEmpty over dates with the filter on the specific year.&lt;/P&gt;
&lt;P&gt;We don't like the fact that we go to the partition twice, essentially for the same data but on different granularity. So we can do a little trick in MDX. Since we will have to go to the Date granularity within a year anyway, let's do it sooner rather than later - and expand granularity already inside Iif check.&amp;nbsp;The resulting MDX will look like following:&lt;/P&gt;&lt;PRE&gt;CREATE
 MEMBER CurrentCube.LastYearIndex AS
  Iif(
   IsEmpty(Aggregate(Descendants([Date ].[Calendar], [Date ].[Calendar].[Date ]), [Measures].[Internet Sales Amount])),
   [Date ].[Calendar Year].PrevMember,
   Rank([Date ].[Calendar Year].CurrentMember, [Date ].[Calendar Year].[Calendar Year])
  )
 SET LastYear AS [Date ].[Calendar Year].[Calendar Year].Item((LastYearIndex, [Date ].[Calendar Year].LastChild)-1)
 SET LastDate AS Tail(NonEmpty([Date ].[Date ].[Date ].MEMBERS, (LastYear, [Measures].[Internet Sales Amount])), 1)
&lt;/PRE&gt;
&lt;P&gt;Let's see what we get in the trace now:&lt;/P&gt;&lt;PRE&gt;Query Begin	0 - MDXQuery	MDX Studio v0.2.6.0
Progress Report Begin	14 - Query	Started reading data from the 'Internet_Sales_2004' partition.
Progress Report End	14 - Query	Finished reading data from the 'Internet_Sales_2004' partition.
Query Subcube	2 - Non-cache data
00000000,000,00000,00,011010000001100000,000000000000000000,000000000000000000,0000000000000000000000,000000000000000000000,00,10
Query Subcube	1 - Cache data
00000000,000,00000,00,010000000000100000,000000000000000000,000000000000000000,0000000000000000000000,000000000000000000000,00,10
Query End	0 - MDXQuery
&lt;/PRE&gt;
&lt;P&gt;This is perfect - exactly what we wanted. The second Query Subcube which used to be Non-cache data, now turned into cached one, and there is only one query to only one partition going on.&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;This post was written as a result of &lt;/EM&gt;&lt;A class="" href="http://www.sql.ru/forum/actualthread.aspx?tid=506477"&gt;&lt;EM&gt;discussion&lt;/EM&gt;&lt;/A&gt;&lt;EM&gt; on sql.ru forum&lt;/EM&gt;&lt;/P&gt;
&lt;BR&gt;&lt;div class = "shareblock"&gt;&lt;span class = "shareblockTitle"&gt;Share this post:&lt;/span&gt;&lt;span class = "shareblockLink"&gt; &lt;a href = "mailto:?subject=MDX and part