<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.adatis.co.uk/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Jeremy Kashel&amp;#39;s Blog : MDX</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDX/default.aspx</link><description>Tags: MDX</description><dc:language>en</dc:language><generator>CommunityServer 2007 SP2 (Build: 20611.960)</generator><item><title>Managing SSAS Named Sets with Master Data Services Collections</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2011/11/07/managing-ssas-named-sets-with-master-data-services-collections.aspx</link><pubDate>Mon, 07 Nov 2011 17:04:00 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:10006</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=10006</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2011/11/07/managing-ssas-named-sets-with-master-data-services-collections.aspx#comments</comments><description>&lt;p&gt;Master Data Services &lt;a href="http://msdn.microsoft.com/en-us/library/ee633733.aspx" target="_blank"&gt;Collections&lt;/a&gt; are probably not the first feature that come to mind when you think of the MDS product. The hierarchies and member grids tend to be the core functionality, but as this post will hopefully show, MDS Collections are useful also.&lt;/p&gt;  &lt;p&gt;Collections are essentially managed lists of members that can come from multiple different MDS explicit hierarchies, or also from another collection. The idea is that this “subset of members” can be maintained in MDS by a business user and then fed to external applications for reporting or other purposes.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Analysis Services Named Sets&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;One example of how collections can be used is to maintain Analysis Services named sets. Some named sets, such as a Top 50 Customers, don&amp;#39;t require any maintenance, as it’s just the top 50 of all customers, based on a particular measure. On the other hand, sometimes named sets can be static lists, e.g. a list of core products that the user wants to see for reporting.&lt;/p&gt;  &lt;p&gt;In the latter example, if a user wants the definition of a named set to change, they have to get IT to change the MDX script. MDS collections can help by allowing the user to control the named set definition, reducing the burden on IT.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Example Scenario&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;  &lt;p&gt;Here’s an example of how this is done. First of all, the end game is that I have a named set in Analysis Services that is currently just for 3 products. Therefore, a user can easily drop this set into an Excel report to get quick access to the products that are important to them:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/image_3AD09CE1.png"&gt;&lt;img style="border:0px currentColor;padding-top:0px;padding-right:0px;padding-left:0px;display:inline;background-image:none;" title="image" border="0" alt="image" src="http://blogs.adatis.co.uk/blogs/jeremykashel/image_thumb_6132802C.png" width="343" height="114" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;So the challenge is that we need to find some way of extending this named set without doing it manually. This is where MDS starts to come in, so using the sample Product model that comes with MDS, I’ve created a new collection called Favourite Products, as shown in the image below:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/image_0008F40B.png"&gt;&lt;img style="border:0px currentColor;padding-top:0px;padding-right:0px;padding-left:0px;display:inline;background-image:none;" title="image" border="0" alt="image" src="http://blogs.adatis.co.uk/blogs/jeremykashel/image_thumb_033ADBF3.png" width="454" height="173" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;If I go and edit the Favourite Products collection in MDS, then I can drag and drop more products into this collection, or remove some existing members. In this case, I’ve chosen to add two new products:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/image_01F64314.png"&gt;&lt;img style="border:0px currentColor;padding-top:0px;padding-right:0px;padding-left:0px;display:inline;background-image:none;" title="image" border="0" alt="image" src="http://blogs.adatis.co.uk/blogs/jeremykashel/image_thumb_1D2E9C15.png" width="380" height="172" /&gt;&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;C#, AMO and the MDS API&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;So the named set represents the target that we need to get to, whereas the MDS collection that’s shown is the source. To get the contents of the MDS collection to update the named set, one way of doing it is to use the MDS API to pick up the collection members, and then to use AMO in order to write the named set into the MDX script. I’m just doing this in a C# windows application, but you could do it via a batch process, such as SSIS. For this post I’m just going to show the code, so here goes:&lt;/p&gt;  &lt;p&gt;This post is already starting to feel too long so I’m not going to show the basics of the MDS API. For that, take a look at a good posting by the MDS Team blog &lt;a href="http://sqlblog.com/blogs/mds_team/archive/2010/01/12/getting-started-with-the-web-services-api-in-sql-server-2008-r2-master-data-services.aspx" target="_blank"&gt;here&lt;/a&gt;. Also, as anyone who knows me will no doubt agree, I don’t tend to get involved in doing C#, so don’t consider this to be production ready! It should give you an idea of the basics though. Anyway, assuming that we’re now connected to the MDS Web Service, I’ve created the following method that will return the members from the collection:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;private &lt;/span&gt;&lt;span&gt;HierarchyMembers &lt;/span&gt;ObtainHierarchyMembers(&lt;span style="color:blue;"&gt;string &lt;/span&gt;entityId, &lt;span style="color:blue;"&gt;string &lt;/span&gt;hierarchyId, &lt;span style="color:blue;"&gt;string &lt;/span&gt;modelId, &lt;span style="color:blue;"&gt;string &lt;/span&gt;versionId)
{
    &lt;span&gt;HierarchyMembersGetRequest &lt;/span&gt;request = &lt;span style="color:blue;"&gt;new &lt;/span&gt;&lt;span&gt;HierarchyMembersGetRequest&lt;/span&gt;();
    &lt;span&gt;HierarchyMembersGetResponse &lt;/span&gt;response = &lt;span style="color:blue;"&gt;new &lt;/span&gt;&lt;span&gt;HierarchyMembersGetResponse&lt;/span&gt;();
    request.HierarchyMembersGetCriteria = &lt;span style="color:blue;"&gt;new &lt;/span&gt;&lt;span&gt;HierarchyMembersGetCriteria&lt;/span&gt;();

    &lt;span style="color:green;"&gt;//State that our hierarhcy type is a collection and that we want collection members
    &lt;/span&gt;request.HierarchyMembersGetCriteria.HierarchyType = &lt;span&gt;HierarchyType&lt;/span&gt;.Collection;
    &lt;span style="color:green;"&gt;//Pass in the key search criteria to identify the correct collection in MDS
    &lt;/span&gt;request.HierarchyMembersGetCriteria.ParentEntityId = &lt;span style="color:blue;"&gt;new &lt;/span&gt;&lt;span&gt;Identifier &lt;/span&gt;{ Name = entityId };
    request.HierarchyMembersGetCriteria.HierarchyId = &lt;span style="color:blue;"&gt;new &lt;/span&gt;&lt;span&gt;Identifier &lt;/span&gt;{ Name = hierarchyId };
    request.HierarchyMembersGetCriteria.ModelId = &lt;span style="color:blue;"&gt;new &lt;/span&gt;&lt;span&gt;Identifier &lt;/span&gt;{ Name = modelId };
    request.HierarchyMembersGetCriteria.VersionId = &lt;span style="color:blue;"&gt;new &lt;/span&gt;&lt;span&gt;Identifier &lt;/span&gt;{ Name = versionId };

    request.HierarchyMembersGetCriteria.RowLimit = 50;
    request.International = &lt;span style="color:blue;"&gt;new &lt;/span&gt;&lt;span&gt;International&lt;/span&gt;();
    &lt;span&gt;OperationResult &lt;/span&gt;result = &lt;span style="color:blue;"&gt;new &lt;/span&gt;&lt;span&gt;OperationResult&lt;/span&gt;();
    &lt;span style="color:green;"&gt;//Return the hierarchy members from the service
    &lt;/span&gt;&lt;span style="color:blue;"&gt;return &lt;/span&gt;service.HierarchyMembersGet(request.International, request.HierarchyMembersGetCriteria, &lt;span style="color:blue;"&gt;out &lt;/span&gt;result);
}&lt;/pre&gt;

&lt;p&gt;Before we use the above method, we need to connect to SSAS using AMO. That can be done quite easily with the following code:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span&gt;Server &lt;/span&gt;server = &lt;span style="color:blue;"&gt;new &lt;/span&gt;&lt;span&gt;Server&lt;/span&gt;();
&lt;span style="color:blue;"&gt;string &lt;/span&gt;connection = &lt;span&gt;&amp;quot;Data Source=.;Catalog=Adventure Works DW 2008R2;&amp;quot;&lt;/span&gt;;
server.Connect(connection);&lt;/pre&gt;

&lt;p&gt;After we’ve done all the usual error handling associated with connecting to a database, we need to pick up the SSAS database and cube:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span&gt;Database &lt;/span&gt;database = server.Databases[&lt;span&gt;&amp;quot;Adventure Works DW 2008R2&amp;quot;&lt;/span&gt;];
&lt;span&gt;Cube &lt;/span&gt;cube = database.Cubes[&lt;span&gt;&amp;quot;Adventure Works&amp;quot;&lt;/span&gt;];&lt;/pre&gt;

&lt;p&gt;Now we’re ready to call our ObtainHierarchyMembers method, before looping through it to build the named set:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span&gt;StringBuilder &lt;/span&gt;mdx = &lt;span style="color:blue;"&gt;new &lt;/span&gt;&lt;span&gt;StringBuilder&lt;/span&gt;(&lt;span&gt;&amp;quot;\n//Auto generated named set at &amp;quot; &lt;/span&gt;+ &lt;span&gt;DateTime&lt;/span&gt;.Now.ToString() + 
    &lt;span&gt;&amp;quot;\nCREATE SET CurrentCube.[Favourite Products] AS {&amp;quot;&lt;/span&gt;);
&lt;span style="color:blue;"&gt;int &lt;/span&gt;count = 1;
&lt;span style="color:green;"&gt;//Loop through the collection to build the mdx
&lt;/span&gt;&lt;span style="color:blue;"&gt;foreach &lt;/span&gt;(&lt;span&gt;ParentChild &lt;/span&gt;pc &lt;span style="color:blue;"&gt;in &lt;/span&gt;hm.Members)
{
    &lt;span style="color:green;"&gt;//Add the members to the MDX string
    //This references the member by name
    //It would be possible to reference by member key, but would require more work
    &lt;/span&gt;mdx.Append(&lt;span&gt;&amp;quot;[Product].[Product].[&amp;quot; &lt;/span&gt;+ pc.Child.Name + &lt;span&gt;&amp;quot;]&amp;quot;&lt;/span&gt;);
    &lt;span style="color:blue;"&gt;if &lt;/span&gt;(count &amp;lt; hm.Members.Count())
    {
        mdx.Append(&lt;span&gt;&amp;quot;, &amp;quot;&lt;/span&gt;);
    }
    count++;
}
mdx.Append(&lt;span&gt;&amp;quot;};&amp;quot;&lt;/span&gt;);&lt;/pre&gt;


&lt;p&gt;Now we need to insert the named set in the correct place within the existing MDX script, bearing in mind it could already exist:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;string &lt;/span&gt;currentScript = cube.MdxScripts[0].Commands[0].Text;
&lt;span style="color:green;"&gt;//Find the correct place to insert the named set within the MDX script:
&lt;/span&gt;&lt;span style="color:blue;"&gt;int &lt;/span&gt;start = currentScript.IndexOf(&lt;span&gt;&amp;quot;\n//Auto generated named set at&amp;quot;&lt;/span&gt;);
&lt;span style="color:blue;"&gt;int &lt;/span&gt;end = 0;
&lt;span&gt;StringBuilder &lt;/span&gt;newScript = &lt;span style="color:blue;"&gt;new &lt;/span&gt;&lt;span&gt;StringBuilder&lt;/span&gt;(currentScript);
&lt;span style="color:blue;"&gt;if &lt;/span&gt;(start != -1)
{
    end = currentScript.IndexOf(&lt;span&gt;&amp;quot;;&amp;quot;&lt;/span&gt;, start);
    &lt;span style="color:green;"&gt;//If the named set already exists, remove it
    &lt;/span&gt;newScript.Remove(start, end - start + 1);
}
&lt;span style="color:blue;"&gt;else
&lt;/span&gt;{
    start = currentScript.Length;
}
&lt;span style="color:green;"&gt;//Insert the named set in the correct place
&lt;/span&gt;newScript.Insert(start, mdx.ToString());
&lt;span style="color:green;"&gt;//Update the cube&amp;#39;s MDX script
&lt;/span&gt;cube.MdxScripts[0].Commands[0].Text = newScript.ToString();&lt;/pre&gt;

&lt;p&gt;Finally we just need to update the cube in order to write the MDX back to the cube:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;//Call the update methods to update the cube
&lt;/span&gt;cube.MdxScripts[0].Update();
cube.Update();&lt;/pre&gt;




&lt;p&gt;&lt;strong&gt;User Reports&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that the cube has been updated, if the Excel report is refreshed, then the two new products will appear in the list:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/image_06F880C3.png"&gt;&lt;img style="border:0px currentColor;padding-top:0px;padding-right:0px;padding-left:0px;display:inline;background-image:none;" title="image" border="0" alt="image" src="http://blogs.adatis.co.uk/blogs/jeremykashel/image_thumb_145E93C9.png" width="348" height="150" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Controlling SSAS named sets like this won’t be for everybody. I’ve certainly worked in a few clients where strict process has to be followed to update this sort of thing, but I can think of other companies that I know where this would be really useful.&lt;/p&gt;

&lt;p&gt;Managing Analysis Services named sets is just one use for collections. Another example might be managing default multi-select parameters for SSRS reports. As collections are just lists of members that can be extracted easily, what you do with them is up to you!&lt;/p&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=10006" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDX/default.aspx">MDX</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Analysis+Services/default.aspx">Analysis Services</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/C_2300_/default.aspx">C#</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDM/default.aspx">MDM</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Master+Data+Services/default.aspx">Master Data Services</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDS/default.aspx">MDS</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Master+Data+Management/default.aspx">Master Data Management</category></item><item><title>Master Data Services Training in the UK</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2011/11/01/sql-server-training-in-london.aspx</link><pubDate>Tue, 01 Nov 2011 23:33:00 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:9986</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=9986</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2011/11/01/sql-server-training-in-london.aspx#comments</comments><description>&lt;p&gt;This is just a quick post to announce a range of SQL Server training courses, organised (and in some cases delivered) by &lt;a href="http://cwebbbi.wordpress.com/" target="_blank"&gt;Chris Webb&lt;/a&gt;. To start off there’s a SQL Server course delivered by Christian Bolton in December, followed by an Analysis Services course delivered by Chris in February. I’ll be delivering a Master Data Services course in February, before Chris delivers an MDX course in March.&lt;/p&gt;  &lt;p&gt;The details for all the courses are:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;SQL Server Internals and Troubleshooting Workshop - Christian Bolton – 6th – 7th December 2011&lt;/strong&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;blockquote&gt;   &lt;p&gt;The Advanced Troubleshooting Workshop for SQL Server 2005, 2008 and R2 provides attendees with SQL Server internals knowledge, practical troubleshooting skills and a proven methodical approach to problem solving. The workshop will enable attendees to tackle complex SQL Server problems with confidence.&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;Full details and registration &lt;a href="http://www.regonline.co.uk/Register/Checkin.aspx?EventID=1016921&amp;amp;trackingcode=ADT" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt; &lt;/blockquote&gt;  &lt;ul&gt;   &lt;li&gt;&lt;b&gt;Real World Cube Design and Performance Tuning with Analysis Services – Chris Webb – February 2012&lt;/b&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;blockquote&gt;   &lt;p&gt;A two day course that takes real world experience in showing you how to build a best practice Analysis Services cube, covering design issues such as data warehouse design and complex cube modelling. Day two then covers performance optimisation for Analysis Services, including MDX optimisation and cube processing.&lt;/p&gt;    &lt;p&gt;Full details and registration &lt;a href="http://www.regonline.co.uk/Register/Checkin.aspx?EventID=1028948&amp;amp;trackingcode=ADT" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt; &lt;/blockquote&gt;  &lt;ul&gt;   &lt;li&gt;&lt;b&gt;Introduction to Master Data Services with Jeremy Kashel – February 2012&lt;/b&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;blockquote&gt;   &lt;p&gt;An end to end look inside Master Data Services, this full day course will begin with a synopsis of Master Data Management before moving on to an overview of Microsoft SQL Server 2008 R2 Master Data Services (MDS). The remainder of the course will cover the major MDS topics, such as modelling and business rules, which will include a number of practical exercises.&lt;/p&gt;    &lt;p&gt;More details and registration for &lt;a href="http://www.regonline.co.uk/Register/Checkin.aspx?EventID=1028960&amp;amp;trackingcode=ADT" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt; &lt;/blockquote&gt;  &lt;ul&gt;   &lt;li&gt;&lt;b&gt;Introduction to MDX with Chris Webb – March 2012&lt;/b&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;blockquote&gt;   &lt;p&gt;The Introduction to MDX course aims to take you from the point of being a complete beginner with no previous knowledge of MDX up to the point where you can write 90% of the MDX calculations and queries you’ll ever need to write. The three day course covers the basics, such as sets, tuples, members to more advanced concepts such as scoped assignments and performance tuning.&lt;/p&gt;    &lt;p&gt;Full details and registration &lt;a href="http://www.regonline.co.uk/Register/Checkin.aspx?EventID=1026958&amp;amp;trackingcode=ADT" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=9986" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDX/default.aspx">MDX</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Analysis+Services/default.aspx">Analysis Services</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/SQL+Server/default.aspx">SQL Server</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Events/default.aspx">Events</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDM/default.aspx">MDM</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDS/default.aspx">MDS</category></item><item><title>MDX with Microsoft SQL Server 2008 R2 Analysis Services Cookbook - Book Review</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2011/10/13/mdx-with-microsoft-sql-server-2008-r2-analysis-services-cookbook-book-review.aspx</link><pubDate>Thu, 13 Oct 2011 16:51:00 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:9924</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=9924</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2011/10/13/mdx-with-microsoft-sql-server-2008-r2-analysis-services-cookbook-book-review.aspx#comments</comments><description>&lt;table cellspacing="0" cellpadding="2"&gt;     &lt;tr&gt;       &lt;td&gt;         &lt;p&gt;As you may have already seen, &lt;a href="http://www.packtpub.com/"&gt;Packt&lt;/a&gt; have released a new MDX book, namely &lt;a href="http://www.packtpub.com/mdx-with-microsoft-sql-server-2008-r2-analysis-services/book?utm_source=packtpub.com&amp;amp;utm_medium=article&amp;amp;utm_content=other&amp;amp;utm_campaign=mdb_009103"&gt;MDX with Microsoft SQL Server 2008 R2 Analysis Services Cookbook&lt;/a&gt; by Tomislav Piasevoli.&lt;/p&gt;          &lt;p&gt;The book is written as cookbook, meaning that you can choose the individual ‘recipes’ that apply to whatever problem you’re trying to solve. Each recipe starts off with a section called ‘Getting Ready’, essentially giving you the base query to use, before moving onto ‘How to do it&amp;#39;, which covers the main instructions for the recipe. There are then further sections,&amp;nbsp;which explain how the example works and also suggest other functions/concepts that you may want to consider. This sort of cookbook style makes it really easy to follow, each recipe is displayed very clearly in the book.&lt;/p&gt;          &lt;p&gt;A wide range of MDX problems are covered, from time calculations to context-aware calculations. Not every piece of the MDX functionality is covered, which is to be expected, given the size of the book. It also doesn’t cover the usual introduction to members/sets etc that MDX books tend to cover, but it’s clearly stated that having a working knowledge of MDX is a pre-requisite for the book.&lt;/p&gt;          &lt;p&gt;I found the copy that I’ve been reading in the Adatis office, but I really do like it, so I’ll definitely be ordering my own copy!&lt;/p&gt;       &lt;/td&gt;        &lt;td align="right"&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/image_60CB45AC.png"&gt;&lt;img style="border:0px currentColor;padding-top:0px;padding-right:0px;padding-left:0px;display:inline;background-image:none;" title="image" border="0" alt="image" src="http://blogs.adatis.co.uk/blogs/jeremykashel/image_thumb_4E827EEA.png" width="188" height="244" /&gt;&lt;/a&gt;&lt;/td&gt;     &lt;/tr&gt;   &lt;/table&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=9924" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDX/default.aspx">MDX</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Analysis+Services/default.aspx">Analysis Services</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Books/default.aspx">Books</category></item><item><title>Master Data Services Training</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2011/08/22/master-data-services-training.aspx</link><pubDate>Mon, 22 Aug 2011 08:49:26 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:9637</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=9637</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2011/08/22/master-data-services-training.aspx#comments</comments><description>&lt;p&gt;As I’m sure you’ve seen, registration for &lt;a href="http://www.sqlbits.com/default.aspx"&gt;SQLBits 9 – Query Across the Mersey&lt;/a&gt; is now open.&lt;/p&gt;  &lt;p&gt;This time around I’ll be running a deep-dive training day with &lt;a href="http://blogs.adatis.co.uk/blogs/timkent/default.aspx"&gt;Tim Kent&lt;/a&gt; on Master Data Services. This will be a full day of training, showing you how MDS can be used to manage the master data in your organisation. We’re going to start by giving an overview of Master Data Management, before moving on to covering the following MDS topics in detail:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Installing, configuring, and maintaining Master Data Services &lt;/li&gt;    &lt;li&gt;Creating and using models &lt;/li&gt;    &lt;li&gt;Version management &lt;/li&gt;    &lt;li&gt;Business rules and workflow &lt;/li&gt;    &lt;li&gt;Importing data into Master Data Services &lt;/li&gt;    &lt;li&gt;Integrating with other systems &lt;/li&gt;    &lt;li&gt;Security &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;You can register for the MDS Training day, or one of the &lt;a href="http://www.sqlbits.com/information/TrainingDay.aspx"&gt;other 10 training days&lt;/a&gt;, by using the following &lt;a href="http://www.regonline.com/Register/Checkin.aspx?EventID=987503"&gt;registration page&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Remember, the early bird discount expires at midnight on Friday this week!&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font color="#ff0000"&gt;Update – &lt;/font&gt;&lt;font color="#000000"&gt;&lt;font color="#ff0000"&gt;We are running another MDS course in February 2012.&lt;/font&gt; &lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2011/11/01/sql-server-training-in-london.aspx"&gt;Click here for the details.&lt;/a&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=9637" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDX/default.aspx">MDX</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Events/default.aspx">Events</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Master+Data+Services/default.aspx">Master Data Services</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDS/default.aspx">MDS</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Master+Data+Management/default.aspx">Master Data Management</category></item><item><title>Allocations in PowerPivot Using DAX</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2010/02/27/allocations-in-powerpivot-using-dax.aspx</link><pubDate>Sat, 27 Feb 2010 14:56:56 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:7569</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=7569</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2010/02/27/allocations-in-powerpivot-using-dax.aspx#comments</comments><description>&lt;p align="justify"&gt;Although I didn&amp;#39;t mention it, the inspiration for my &lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2010/02/11/dax-closing-balances.aspx"&gt;last post&lt;/a&gt; was the excellent &lt;a href="http://eu.wiley.com/WileyCDA/WileyTitle/productCd-0471748080.html" target="_blank"&gt;MDX Solutions 2nd Edition&lt;/a&gt;. Whilst flicking through the book a while back, I though it would be interesting to see how DAX and PowerPivot could handle the so called &amp;#39;Common Calculations and Selections in MDX&amp;#39;.&lt;/p&gt;  &lt;p align="justify"&gt;This post continues that theme, focusing on one calculation in particular that&amp;#39;s in the aforementioned MDX book, namely &amp;#39;Unweighted Allocations down the Hierarchy&amp;#39;. Essentially this business problem is dealing with allocating data that is entered at a higher level (e.g. Quarter) down to a lower level (e.g. Day). It varies from business to business what the users actually want to see at the day level in this situation. Some expect to see the Quarter amount, some want to see a blank value and then some users want the amount in the quarters allocated down to the day level.&lt;/p&gt;  &lt;p align="justify"&gt;If the expectation is that data should be allocated, then one way of doing the allocation is to use a ratio. In the MDX book, this is achieved by the following MDX:&lt;/p&gt;  &lt;pre class="code"&gt;1.0 / 
&lt;span style="color:blue;"&gt;Descendants&lt;/span&gt;(
            &lt;span style="color:maroon;"&gt;Ancestor&lt;/span&gt;(
                    [Date].[Calendar].&lt;span style="color:maroon;"&gt;CurrentMember&lt;/span&gt;,
                    [Date].[Calendar].[Calendar Quarter]
                    ),
            [Date].[Calendar].&lt;span style="color:maroon;"&gt;CurrentMember&lt;/span&gt;.&lt;span style="color:blue;"&gt;Level&lt;/span&gt;,
            &lt;span style="color:blue;"&gt;SELF
            &lt;/span&gt;).&lt;span style="color:blue;"&gt;Count&lt;/span&gt;&lt;/pre&gt;

&lt;p align="justify"&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;If we put this into a query and run it against the Calendar hierarchy in AdventureWorksDW we get the following results:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/AllocationsinPowerPivotUsingDAX_10A07/image_8.png"&gt;&lt;img style="border-right:0px;border-top:0px;border-left:0px;border-bottom:0px;" height="184" alt="image" src="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/AllocationsinPowerPivotUsingDAX_10A07/image_thumb_3.png" width="295" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p align="justify"&gt;Therefore our quarter-entered value can simply be multiplied by this ratio to get the correct value at each level in the Calendar hierarchy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PowerPivot&lt;/strong&gt;&lt;/p&gt;

&lt;p align="justify"&gt;So the challenge is how to be able to do this in PowerPivot. I started off by looking for some data in AdventureWorksDW that would fit the bill. A good candidate seems to be the FactSalesQuota table, which I imported into PowerPivot along with its related dimensions:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/AllocationsinPowerPivotUsingDAX_10A07/image_4.png"&gt;&lt;img style="border-right:0px;border-top:0px;border-left:0px;border-bottom:0px;" height="267" alt="image" src="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/AllocationsinPowerPivotUsingDAX_10A07/image_thumb_1.png" width="686" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p align="justify"&gt;Once in the pivot table, I wanted to be able to calculate the correct ratio at the daily and monthly levels. For the daily level this is 1 / 90, as there are 90 days in the Q1 that year, and for the month level it is 1/3 as there are 3 months in the quarter.&lt;/p&gt;

&lt;p align="justify"&gt;Given that there&amp;#39;s no MDX style Ancestor() function in DAX, I ended up having to have a different calculation at different levels. Something that I learnt from &lt;a href="http://social.msdn.microsoft.com/Forums/en-US/sqlkjpowerpivotforexcel/thread/99a73152-bbd5-41f3-88c7-054e8988f395" target="_blank"&gt;this forum post&lt;/a&gt; is that you need to use IF() in order to isolate a calculation at a different level. Therefore the DAX that I came up with was:&lt;/p&gt;

&lt;p&gt;=IF( COUNTROWS(VALUES(&amp;#39;DimDate&amp;#39;[FullDateAlternateKey])) = 1, &lt;/p&gt;

&lt;p&gt;&amp;#160;&amp;#160;&amp;#160; 1 / CALCULATE( COUNTROWS(&amp;#39;DimDate&amp;#39;), ALL(&amp;#39;DimDate&amp;#39;[FullDateAlternateKey], &amp;#39;DimDate&amp;#39;[EnglishMonthName]) ), 
  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; 1 / CALCULATE( COUNTROWS( DISTINCT( &amp;#39;DimDate&amp;#39;[EnglishMonthName]) ), ALL(&amp;#39;DimDate&amp;#39;[EnglishMonthName]) ) 

  &lt;br /&gt;&amp;#160;&amp;#160; )&lt;/p&gt;

&lt;p&gt;The COUNTROWS(VALUES(&amp;#39;DimDate&amp;#39;[FullDateAlternateKey])) = 1 allows me to check that we&amp;#39;re at the day level, and if we are at the day level, then count the number of days in the quarter. Otherwise, if we&amp;#39;re at the month level we calculate the number of months in the current quarter. When this DAX is put into a new measure we get the following results:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/AllocationsinPowerPivotUsingDAX_10A07/image_6.png"&gt;&lt;img style="border-right:0px;border-top:0px;border-left:0px;border-bottom:0px;" height="206" alt="image" src="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/AllocationsinPowerPivotUsingDAX_10A07/image_thumb_2.png" width="402" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p align="justify"&gt;This produces the same results as the MDX statement. The next step was then to apply this ratio to the Sales Amount Quota for the current quarter. This was achieved using the following DAX:&lt;/p&gt;

&lt;p&gt;=IF(
  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; COUNTROWS( VALUES(&amp;#39;DimDate&amp;#39;[FullDateAlternateKey]) ) &amp;lt; 90,

  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; CALCULATE( Sum(&amp;#39;FactSalesQuota&amp;#39;[SalesAmountQuota]),&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; All(&amp;#39;DimDate&amp;#39;[FullDateAlternateKey], &amp;#39;DimDate&amp;#39;[EnglishMonthName]) ) * &amp;#39;FactSalesQuota&amp;#39;[Ratio],

  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Sum(&amp;#39;FactSalesQuota&amp;#39;[SalesAmountQuota])

  &lt;br /&gt;&amp;#160;&amp;#160; )&lt;/p&gt;

&lt;p align="justify"&gt;The &amp;lt; 90 at the start of the statement allows me to check if we&amp;#39;re at the day level or the month level. If we&amp;#39;re at either of these levels, then we want to pick up the Quarter-level SalesAmountQuota and multiply by the ratio. Otherwise, at the Quarter and Year levels, we just apply the original SalesAmountQuota value. This produces the following results:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/AllocationsinPowerPivotUsingDAX_10A07/image_10.png"&gt;&lt;img style="border-right:0px;border-top:0px;border-left:0px;border-bottom:0px;" height="203" alt="image" src="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/AllocationsinPowerPivotUsingDAX_10A07/image_thumb_4.png" width="542" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p align="left"&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p align="justify"&gt;The use of the &lt;a href="http://msdn.microsoft.com/en-us/library/ms145989.aspx" target="_blank"&gt;MDX Scope statement&lt;/a&gt; would have been nice to pinpoint exactly where I wanted the calculation to occur, but then again, the use of IF() keeps it quite simple, which is better for an Excel power user. Whilst MDX is very very powerful, can you imagine a business user getting to grips with MDX statements such as Scope or Freeze?&lt;/p&gt;

&lt;p align="justify"&gt;The bottom line is that the DAX calculation produces exactly what I want - the new AllocatedSalesQuota measure is correct at all levels in the Calendar hierarchy. Even with an IF() statement, like everything else in PowerPivot, the calculation performs very quickly.&lt;/p&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=7569" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDX/default.aspx">MDX</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Analysis+Services/default.aspx">Analysis Services</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Excel/default.aspx">Excel</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/PowerPivot/default.aspx">PowerPivot</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Gemini/default.aspx">Gemini</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/DAX/default.aspx">DAX</category></item><item><title>Entering Dates in PPS Planning Assignments</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/11/27/entering-dates-in-pps-planning-assignments.aspx</link><pubDate>Thu, 27 Nov 2008 09:14:57 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:7472</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=7472</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/11/27/entering-dates-in-pps-planning-assignments.aspx#comments</comments><description>&lt;p&gt;In the recent PPS Planning projects that I&amp;#39;ve been involved in, the challenges have often been around subjects such as business rules, hence the often recurring theme of this blog. Recently the tables were turned though, as I was told by a user that they wanted to enter dates into a PPS assignment. &lt;/p&gt;  &lt;p&gt;I was initially a bit concerned that the Excel add-in may not be able to deliver here - after all its great at capturing numbers, but knowing the rigid structure of the fact tables, I couldn&amp;#39;t see how it would manage to store a date. Then I remembered something from my VBA days many years ago - that is that Excel stores dates as a number from 30/12/1899, meaning in theory it should be possible to get dates working in PPS. Thankfully it is possible, as this post explains.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Excel Setup&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;The first step to get this working when designing your form template is to set the matrix to have a matrix style of &amp;#39;none&amp;#39;. If you don&amp;#39;t do this, then the built-in matrix styles will over-ride your formatting changes to the required cells. Speaking of formatting, the next step is to format the data entry cells that will contain dates, just using the standard Excel formatting window:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/EnteringDatesinPPSPlanningAssignments_12FBC/image_2.png"&gt;&lt;img style="border-top-width:0px;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;" height="373" alt="image" src="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/EnteringDatesinPPSPlanningAssignments_12FBC/image_thumb.png" width="519" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Once these few simple steps are done, then the assignment will behave just like any other. As the date is stored as a number, the numeric representation of the date will end up in the fact table just as any other piece of data.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Dates in Business Rules&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Once the numbers are in the fact table, we need to convert them to dates to use them in business rules in some way. We can&amp;#39;t do much in PEL unfortunately, so the options are either NativeMDX or NativeSQL.&lt;/p&gt;  &lt;p&gt;As Analysis Services can pickup some of the VBA functions, it&amp;#39;s possible to use the &lt;a href="http://www.techonthenet.com/excel/formulas/dateadd.php" target="_blank"&gt;VBA DateAdd() function&lt;/a&gt; to convert the stored number back into a date. So in the example below, I&amp;#39;m using the DateAdd() function to convert the number to a date, before comparing the resulting date against another date using the VBA DateDiff() function:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;WITH 
MEMBER    &lt;/span&gt;[Measures].[DateExample] &lt;span style="color:blue;"&gt;AS &lt;/span&gt;VBA!DateAdd(&amp;quot;d&amp;quot;, [Measures].[Value], &amp;quot;30/12/1899&amp;quot;)
&lt;span style="color:blue;"&gt;MEMBER    &lt;/span&gt;[Measures].[DateDiff] &lt;span style="color:blue;"&gt;AS &lt;/span&gt;VBA!DateDiff(&amp;quot;d&amp;quot;, [Measures].[DateExample], &amp;quot;01/07/1987&amp;quot;)
&lt;span style="color:blue;"&gt;SELECT    Descendants&lt;/span&gt;([Time].[Monthly].[Year].&amp;amp;[2008],,&lt;span style="color:blue;"&gt;leaves&lt;/span&gt;) &lt;span style="color:blue;"&gt;ON &lt;/span&gt;0
&lt;span style="color:blue;"&gt;FROM      &lt;/span&gt;[Strategic Planning]
&lt;span style="color:blue;"&gt;WHERE     &lt;/span&gt;([Account].[Profit and Loss].&amp;amp;[5010], [Measures].[DateDiff], [Entity].[Divisions].&amp;amp;[5003])&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Although the above is just a simple example, it should give you the idea of the kind of calculations that can be performed in Analysis Services. It&amp;#39;s possible to use these functions via a NativeMDXScript or a NativeMDXQuery.&lt;/p&gt;

&lt;p&gt;It&amp;#39;s a similar story with SQL, as it also has its own &lt;a href="http://msdn.microsoft.com/en-us/library/ms186819.aspx" target="_blank"&gt;DateAdd() function&lt;/a&gt;, as shown in the simple select statement below:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;SELECT    &lt;/span&gt;&lt;span style="color:magenta;"&gt;DateAdd&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;d&lt;span style="color:gray;"&gt;, &lt;/span&gt;[Value]&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:red;"&gt;&amp;#39;30/12/1899&amp;#39;&lt;/span&gt;&lt;span style="color:gray;"&gt;)
&lt;/span&gt;&lt;span style="color:blue;"&gt;FROM      &lt;/span&gt;dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;[MG_Strategic Planning_MeasureGroup_default_partition]
&lt;span style="color:blue;"&gt;WHERE     &lt;/span&gt;Scenario_memberid &lt;span style="color:gray;"&gt;= &lt;/span&gt;4 &lt;span style="color:gray;"&gt;AND &lt;/span&gt;Account_MemberId &lt;span style="color:gray;"&gt;= &lt;/span&gt;5010&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;So it&amp;#39;s a shame that PEL can&amp;#39;t work with dates, but the fact that both the database engine and Analysis Services have a DateAdd function means that it&amp;#39;s possible to use dates for logic in both definition and procedural business rules.&lt;/p&gt;

&lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:f88f1694-887d-4e61-bc4f-fa717d954896" style="padding-right:0px;display:inline;padding-left:0px;padding-bottom:0px;margin:0px;padding-top:0px;"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/PerformancePoint" rel="tag"&gt;PerformancePoint&lt;/a&gt;,&lt;a href="http://technorati.com/tags/MDX" rel="tag"&gt;MDX&lt;/a&gt;,&lt;a href="http://technorati.com/tags/SQL" rel="tag"&gt;SQL&lt;/a&gt;,&lt;a href="http://technorati.com/tags/VBA" rel="tag"&gt;VBA&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Excel" rel="tag"&gt;Excel&lt;/a&gt;&lt;/div&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=7472" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDX/default.aspx">MDX</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/PerformancePoint/default.aspx">PerformancePoint</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Excel/default.aspx">Excel</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/SQL/default.aspx">SQL</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/VBA/default.aspx">VBA</category></item><item><title>NativeMDXQuery Business Rules in PerformancePoint Planning</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/10/24/nativemdxquery-business-rules-in-performancepoint-planning.aspx</link><pubDate>Fri, 24 Oct 2008 10:25:53 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:7440</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=7440</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/10/24/nativemdxquery-business-rules-in-performancepoint-planning.aspx#comments</comments><description>&lt;p&gt;Having &lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/08/05/using-nativesql-business-rules-for-seeding-or-what-ifs.aspx" target="_blank"&gt;posted about NativeSql business rules a while back&lt;/a&gt;, I though that I might as well cover NativeMdxQuery business rules also, especially as there isn&amp;#39;t too much documentation available on the web for this subject.&lt;/p&gt;  &lt;p&gt;NativeMdxQuery is a rule implementation type that can be used with assignment rules in PPS Planning. Rather than writing PEL and having the compiler convert your PEL into SQL or MDX, the idea is that you write the raw MDX directly if you pick NativeMdxQuery.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Why Native Rules?&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;When when I posted about NativeSql, I mentioned a scenario or two where using NativeSql was very useful. The advantages to writing a NativeMdxQuery, however, are less obvious. This is especially the case when you consider that a)standard MDX rules in PPS Planning are less restrictive that the SQL rules, and b) that PEL is very efficient - you can write a very concise MDX PEL rule vs writing the equivalent in MDX itself.&lt;/p&gt;  &lt;p&gt;So what advantages are there? Although a fair amount of MDX functions are included in PEL, it&amp;#39;s not possible to use all MDX functions/statements/operators, e.g. Case(), Item(). This is one situation where you might want to write a NativeMdxQuery.&lt;/p&gt;  &lt;p&gt;Also, the ability to filter and restrict data in a raw MDX statement is far more powerful than the options available in PEL. For example, you can use the Filter()/IIF functions in PEL, but they&amp;#39;re quite slow and you&amp;#39;re restricted as to where you can put them. If you use MDX, you can use both a WHERE clause to slice the query and/or a HAVING clause for filtering.&lt;/p&gt;  &lt;p&gt;Finally, when just writing queries from an SSAS cube, a technique that&amp;#39;s sometimes used is to create &lt;a href="http://msdn.microsoft.com/en-us/library/ms146017.aspx" target="_blank"&gt;query-scoped calculated members&lt;/a&gt;, by using the MDX statement WITH MEMBER. This allows you to have calculation steps in your MDX query, and can effectively be used in one sense as a temporary variable store for calculations. If you&amp;#39;re trying to do complex calculations in PEL, you have to assign everything to the &amp;#39;this&amp;#39; keyword. It&amp;#39;s much cleaner to have a block of code where you can define any calculations that the main query depends on. This is what WITH MEMBER will let you do.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;How to Write a NativeMdxQuery&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;The requirements for a business rule using NativeMdxQuery are that you need to write an MDX select statement, which specifies the target cells that will be written to. Unlike NativeSql statements, you do not need to handle how the data gets inserted - PerformancePoint handles all that for you, as long as you produce the MDX select statement.&lt;/p&gt;  &lt;p&gt;An example select statement that makes up the entire content of a NativeMdxQuery assignment rule is shown below. The comments show what the query is doing:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:green;"&gt;--[Measures].[Last Year] just makes the rule a bit more readable
&lt;/span&gt;&lt;span style="color:blue;"&gt;WITH MEMBER &lt;/span&gt;[Measures].[Last Year] &lt;span style="color:blue;"&gt;AS &lt;/span&gt;([Measures].[Value], [Time].[Monthly].&lt;span style="color:maroon;"&gt;CurrentMember&lt;/span&gt;.&lt;span style="color:blue;"&gt;Lag&lt;/span&gt;(12))
&lt;span style="color:blue;"&gt;CELL CALCULATION &lt;/span&gt;queryCalc 
&lt;span style="color:blue;"&gt;FOR 
 &lt;/span&gt;&lt;span style="color:green;"&gt;--This specifices the cells that we are overwriting with an expression or value
 &lt;/span&gt;&amp;#39;([Measures].[Value],
 [Scenario].[All Members].[Scenario].&amp;amp;[1],
 {[Time].[Monthly].[Month].&amp;amp;[200901],[Time].[Monthly].[Month].&amp;amp;[200902]},
 {[Account].[Profit and Loss].[Level 07].&amp;amp;[5010],[Account].[Profit and Loss].[Level 07].&amp;amp;[5009]},
 [BusinessProcess].[Standard].[Level 06].&amp;amp;[8],
 [TimeDataView].[All Members].[TimeDataView].&amp;amp;[1],
 Descendants([Entity].[Divisions].[(All)].&amp;amp;[0], ,leaves),
 Descendants([Currency].[All Members].[(All)].&amp;amp;[0], ,leaves),
 Descendants([Product].[Product Category].[(All)].&amp;amp;[0], ,leaves))&amp;#39; 
&lt;span style="color:blue;"&gt;AS
 &lt;/span&gt;&lt;span style="color:green;"&gt;--The 100 is the value that we are giving the cells above
 &lt;/span&gt;100
&lt;span style="color:green;"&gt;--This is the select statement which cells will receive the value above
&lt;/span&gt;&lt;span style="color:blue;"&gt;SELECT NON EMPTY 
 &lt;/span&gt;([Measures].[Value], 
 ({[Scenario].[All Members].[Scenario].&amp;amp;[1]},
 {{[Time].[Monthly].[Month].&amp;amp;[200901],[Time].[Monthly].[Month].&amp;amp;[200902]}},
 {{[Account].[Profit and Loss].[Level 07].&amp;amp;[5010],[Account].[Profit and Loss].[Level 07].&amp;amp;[5009]}},
 {[BusinessProcess].[Standard].[Level 06].&amp;amp;[8]},
 {[TimeDataView].[All Members].[TimeDataView].&amp;amp;[1]},
 {&lt;span style="color:blue;"&gt;Descendants&lt;/span&gt;([Entity].[Divisions].[(All)].&amp;amp;[0], ,&lt;span style="color:blue;"&gt;leaves&lt;/span&gt;)},
 {&lt;span style="color:blue;"&gt;Descendants&lt;/span&gt;([Currency].[All Members].[(All)].&amp;amp;[0], ,&lt;span style="color:blue;"&gt;leaves&lt;/span&gt;)},
 {&lt;span style="color:blue;"&gt;Descendants&lt;/span&gt;([Product].[Product Category].[(All)].&amp;amp;[0], ,&lt;span style="color:blue;"&gt;leaves&lt;/span&gt;)}))
 &lt;span style="color:green;"&gt;--Ensure we only write to cells with a certain value by using HAVING
 &lt;/span&gt;&lt;span style="color:blue;"&gt;HAVING &lt;/span&gt;([Measures].[Last Year] &amp;gt; 100000)
 &lt;span style="color:blue;"&gt;properties &lt;/span&gt;[Scenario].[All Members].&lt;span style="color:blue;"&gt;Key &lt;/span&gt;,
 [Time].[Monthly].&lt;span style="color:blue;"&gt;Key &lt;/span&gt;,
 [Account].[Profit and Loss].&lt;span style="color:blue;"&gt;Key &lt;/span&gt;,
 [BusinessProcess].[Standard].&lt;span style="color:blue;"&gt;Key &lt;/span&gt;,
 [Entity].[Divisions].&lt;span style="color:blue;"&gt;Key &lt;/span&gt;,
 [TimeDataView].[All Members].&lt;span style="color:blue;"&gt;Key &lt;/span&gt;,
 [Currency].[All Members].&lt;span style="color:blue;"&gt;Key &lt;/span&gt;,
 [Product].[Product Category].&lt;span style="color:blue;"&gt;Key 
 ON COLUMNS 
FROM &lt;/span&gt;[Strategic Planning]
&lt;span style="color:green;"&gt;--Filter on a dimension member property by using a WHERE CLAUSE
&lt;/span&gt;&lt;span style="color:blue;"&gt;WHERE &lt;/span&gt;([Entity].[Region].&amp;amp;[North])&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The points to note about the above statement are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You must connect to the correct cube for the current model;&lt;/li&gt;

  &lt;li&gt;You don&amp;#39;t need to include a &lt;a href="http://msdn.microsoft.com/en-us/library/ms144865(SQL.90).aspx" target="_blank"&gt;cell calculation&lt;/a&gt; - but it&amp;#39;s the way that Microsoft implement business rules that use MDX, and it&amp;#39;s hard to see how you would get a rule to be of any use without it;&lt;/li&gt;

  &lt;li&gt;You must include the member keys as properties, otherwise the rule will error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Writing MDX is clearly not for all users of PerformancePoint, but does provide the ultimate in flexibility when compared to PEL. Most MDX queries written in PPS will use cell calculations. If you&amp;#39;re not used to how these work, or you just want to save some time, remember that an easy way to get started is to use the debug button on an MDX PEL rule in PBM - this will output a query that is similar to the one shown above.&lt;/p&gt;

&lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:4a2485c2-9a5c-4423-9b9e-c99e71038deb" style="padding-right:0px;display:inline;padding-left:0px;float:none;padding-bottom:0px;margin:0px;padding-top:0px;"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/PerformancePoint" rel="tag"&gt;PerformancePoint&lt;/a&gt;,&lt;a href="http://technorati.com/tags/PPS" rel="tag"&gt;PPS&lt;/a&gt;,&lt;a href="http://technorati.com/tags/MDX" rel="tag"&gt;MDX&lt;/a&gt;,&lt;a href="http://technorati.com/tags/NativeMdxQuery" rel="tag"&gt;NativeMdxQuery&lt;/a&gt;&lt;/div&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=7440" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDX/default.aspx">MDX</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/PerformancePoint/default.aspx">PerformancePoint</category></item><item><title>Analysis Services Properties for PerformancePoint MDX Rules</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/09/07/analysis-services-properties-for-performancepoint-mdx-rules.aspx</link><pubDate>Sun, 07 Sep 2008 20:55:05 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:7405</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=7405</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/09/07/analysis-services-properties-for-performancepoint-mdx-rules.aspx#comments</comments><description>&lt;p&gt;One of the great features of PEL is that you can choose either a SQL or &lt;a href="http://msdn.microsoft.com/en-us/library/bb839261.aspx" target="_blank"&gt;MDX implementation&lt;/a&gt; for your rules, depending on what you want to achieve. Whilst the MDX rules are much less restrictive than the SQL rules, they can sometimes run slower, depending of course on how your environment is set up.&lt;/p&gt;  &lt;p align="left"&gt;When the MDX rules do take a long time to run, it&amp;#39;s possible that you might see this message:&lt;/p&gt;  &lt;p align="center"&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/AnalysisServicesPropertiesforPerformance_11E76/clip_image002_2.jpg"&gt;&lt;img style="border-top-width:0px;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;" height="182" alt="clip_image002" src="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/AnalysisServicesPropertiesforPerformance_11E76/clip_image002_thumb.jpg" width="420" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;What has essentially happened within Analysis Services is that an object (e.g. a cube or dimension) has been processed and is waiting to commit. Unfortunately this is not possible whilst an existing query is running, so therefore AS waits for the query to complete. It will, however, only wait so long, which is defined by one of the &lt;a href="http://www.microsoft.com/technet/prodtechnol/sql/2005/ssasproperties.mspx" target="_blank"&gt;Analysis Services Properties&lt;/a&gt; called ForceCommitTimeout. Once this threshold has been reached, then the offending query is canceled, resulting in the error message above.&lt;/p&gt;  &lt;p&gt;Finding the right balance for the Analysis Services ForceCommitTimeout and the PerformancePoint PAC &amp;#39;OLAP Cube Refresh Interval&amp;#39; setting is key. If you have set PPS to re-process its cubes too often then you may well see the above message. On the other hand, if you set the ForceCommitTimeout too high, then queries executed whilst the cube is waiting to commit will be made to wait, meaning your big query will get though ok, but other users may see bad performance.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://geekswithblogs.net/darrengosbell/Default.aspx" target="_blank"&gt;Darren Gosbell&lt;/a&gt; has written an excellent post &lt;a href="http://geekswithblogs.net/darrengosbell/archive/2007/04/24/SSAS-Processing-ForceCommitTimeout-and-quotthe-operation-has-been-cancelledquot.aspx" target="_blank"&gt;here&lt;/a&gt; that provides a thorough explanation of ForceCommitTimeout and other related properties.&lt;/p&gt;  &lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:93433136-881d-4785-93d0-9fce7e82b0f8" style="padding-right:0px;display:inline;padding-left:0px;padding-bottom:0px;margin:0px;padding-top:0px;"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/Analysis%20Services" rel="tag"&gt;Analysis Services&lt;/a&gt;,&lt;a href="http://technorati.com/tags/MDX" rel="tag"&gt;MDX&lt;/a&gt;,&lt;a href="http://technorati.com/tags/PerformancePoint" rel="tag"&gt;PerformancePoint&lt;/a&gt;,&lt;a href="http://technorati.com/tags/PPS" rel="tag"&gt;PPS&lt;/a&gt;&lt;/div&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=7405" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDX/default.aspx">MDX</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Analysis+Services/default.aspx">Analysis Services</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/PerformancePoint/default.aspx">PerformancePoint</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/PEL/default.aspx">PEL</category></item><item><title>BI Evening, TVP, 17th July 2008</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/07/04/bi-evening-tvp-17th-july-2008.aspx</link><pubDate>Fri, 04 Jul 2008 09:06:01 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:6994</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=6994</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/07/04/bi-evening-tvp-17th-july-2008.aspx#comments</comments><description>&lt;p&gt;I&amp;#39;ll be giving a talk titled &lt;strong&gt;&amp;#39;PEL Vs MDX&amp;#39;&lt;/strong&gt; at the UK SQL Server Community&amp;#39;s BI Evening on the 17th July at Microsoft&amp;#39;s TVP offices in Reading.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;The content will be geared towards MS BI developers, and will highlight the differences between the two languages, with the aim that those with MDX experience will be able to make a fast start with PEL.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;Also speaking is Jeremy Kirkup from TAH, on &lt;strong&gt;&amp;#39;Using Excel Services with Analysis Services and MOSS&amp;#39;&lt;/strong&gt;.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;If you are just starting to explore delivering BI solutions with Excel Services and SharePoint then there are some issues that it is wise to be aware of in advance. This session will describe some real world lessons gained while creating BI solutions which expose Analysis Services data through the Excel Web Access web part.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;The Excel 2007 client has first class support for the some of the advanced features of Analysis services such as drill-through.&amp;#160; However, when exposing pivot tables to a browser through the Excel Web Access web part this feature is not available. The session will discuss a couple of approaches to implementing drill-through functionality for EWA based pivot tables using the MOSS SmartPart, AJAX and the Excel Web Services.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;You can register for the event &lt;a href="http://www.sqlserverfaq.com/" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt;  &lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:74e8e948-e9f8-448b-a4d0-b652f3f84af3" style="padding-right:0px;display:inline;padding-left:0px;padding-bottom:0px;margin:0px;padding-top:0px;"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/PEL" rel="tag"&gt;PEL&lt;/a&gt;,&lt;a href="http://technorati.com/tags/MDX" rel="tag"&gt;MDX&lt;/a&gt;,&lt;a href="http://technorati.com/tags/PerformancePoint" rel="tag"&gt;PerformancePoint&lt;/a&gt;,&lt;a href="http://technorati.com/tags/BI%20Evening" rel="tag"&gt;BI Evening&lt;/a&gt;&lt;/div&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=6994" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDX/default.aspx">MDX</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/PEL/default.aspx">PEL</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Events/default.aspx">Events</category></item><item><title>PEL Allocate Statement Vs Associations</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/04/06/pel-allocate-statement-vs-associations.aspx</link><pubDate>Sun, 06 Apr 2008 22:33:00 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:3382</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>3</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=3382</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/04/06/pel-allocate-statement-vs-associations.aspx#comments</comments><description>&lt;p&gt;PerformancePoint provides you with a few options when you want to move data between models, the most obvious of which is Associations. However, there is also the powerful PEL Allocate statement....&lt;/p&gt;
&lt;p&gt;So which method should you use to move data? Associations are very easy to setup and will transfer your data for you - all out of the box. However, they do have a few limitations, namely:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can only apply a minimum, maximum or total aggregation to the source data; &lt;/li&gt;
&lt;li&gt;You can&amp;#39;t have parameters to dynamically select members;&lt;/li&gt;
&lt;li&gt;If you delete a dimension that&amp;#39;s used in an association, then you can&amp;#39;t view or edit the association. Although there should hopefully be no need to do this in production(!), it can be a bit annoying during development.&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;By writing a PEL Data Movement rule of type PushCrossModelAllocation or PullCrossModelAllocation, we get bit more flexibility. With the PEL Allocate statement you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use PEL functions or operators to alter the source value that gets written to the target model; &lt;/li&gt;
&lt;li&gt;Similarly, apply a ratio to the source value so that we only transfer the relevant portion of the whole data value. E.g. we can transfer 20% of head office costs; &lt;/li&gt;
&lt;li&gt;Use rule parameters to dynamically select members. &lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;So if you find that Associations don&amp;#39;t quite do what you need, then how do you use a PEL Allocate rule? The following is an example of a simple PushCrossModelAllocation PEL Allocate statement:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="COLOR:blue;"&gt;scope 
&lt;/span&gt;&lt;span style="COLOR:black;"&gt;(
  &lt;/span&gt;&lt;span style="COLOR:teal;"&gt;$TransferScenario$&lt;/span&gt;&lt;span style="COLOR:black;"&gt;,
  &lt;/span&gt;&lt;span style="COLOR:purple;"&gt;[Time].[Monthly].[All]&lt;/span&gt;&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;&lt;span style="COLOR:black;"&gt;LeafMembers,
  &lt;/span&gt;&lt;span style="COLOR:purple;"&gt;[Organisation].[Europe].[France]&lt;/span&gt;&lt;span style="COLOR:black;"&gt;,
  &lt;/span&gt;&lt;span style="COLOR:purple;"&gt;[Account].[All Members].[Total Cost]&lt;/span&gt;&lt;span style="COLOR:black;"&gt;
&lt;/span&gt;&lt;span style="COLOR:black;"&gt;) ;
     
  &lt;/span&gt;&lt;span style="COLOR:blue;"&gt;Allocate
  &lt;/span&gt;&lt;span style="COLOR:black;"&gt;(
    &lt;/span&gt;&lt;span style="COLOR:green;"&gt;//The scope of cells that will receive the data
    //The this keyword indicates the target is the same cells as defined in the scope
    &lt;/span&gt;&lt;span style="COLOR:blue;"&gt;this&lt;/span&gt;&lt;span style="COLOR:black;"&gt;,
    &lt;/span&gt;&lt;span style="COLOR:green;"&gt;//The source value. If its the same as the scope then we just use ()
    //Or we can apply a simple calculation
    &lt;/span&gt;&lt;span style="COLOR:purple;"&gt;[Account].[All Members].[Volume] &lt;/span&gt;&lt;span style="COLOR:gray;"&gt;* &lt;/span&gt;&lt;span style="COLOR:purple;"&gt;[Account].[All Members].[Cost Per Unit]&lt;/span&gt;&lt;span style="COLOR:black;"&gt;,
    &lt;/span&gt;&lt;span style="COLOR:green;"&gt;//The scaling ratio that gets applied to the source value
    //0.2 indicates that we only want to apply a fraction of the source value
    &lt;/span&gt;&lt;span style="COLOR:black;"&gt;0.2,
    &lt;/span&gt;&lt;span style="COLOR:green;"&gt;//The last two arguments are only relevant for Push and PullCrossModelAllocation
    //They specify the mappings between the source and target members
    //Here I want France (in the source) to be mapped to France (in the target)
    &lt;/span&gt;&lt;span style="COLOR:black;"&gt;(
      &lt;/span&gt;&lt;span style="COLOR:purple;"&gt;[Organisation].[Europe].[France]  
    &lt;/span&gt;&lt;span style="COLOR:black;"&gt;),
    (
      &lt;/span&gt;&lt;span style="COLOR:purple;"&gt;[Organisation].[Group].[France]
    &lt;/span&gt;&lt;span style="COLOR:black;"&gt;)
  ) ;

&lt;/span&gt;&lt;span style="COLOR:blue;"&gt;end scope&lt;/span&gt;&lt;span style="COLOR:black;"&gt;;
&lt;/span&gt;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;
&lt;p&gt;There are a fair few arguments to the Allocate statement, but I&amp;#39;ve found that a good way to learn what its doing is to debug the rule in order to see the MDX statement that gets generated. The MDX query that gets generated for the above PEL statement is as follows:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="COLOR:blue;"&gt;WITH CELL CALCULATION &lt;/span&gt;queryCalc 
&lt;span style="COLOR:blue;"&gt;FOR
 &lt;/span&gt;&lt;span style="COLOR:green;"&gt;//This part of the MDX comes from the 1st argument in the PEL statement
 &lt;/span&gt;&amp;#39;([Account].[All Members].[Account].&amp;amp;[5005],
 [Measures].[Value],
 [Scenario].[All Members].[Scenario].&amp;amp;[1],
 Descendants([Time].[Monthly].[(All)].&amp;amp;[0], 1073741823, LEAVES),
 [Organisation].[Europe].[Level 02].&amp;amp;[11])&amp;#39; 
&lt;span style="COLOR:blue;"&gt;AS
 &lt;/span&gt;&lt;span style="COLOR:green;"&gt;//This is arguments 2 and 3 of the PEL Allocate statement
 //Namely the simple A*B calculation and then multiplying the value by the ratio
 &lt;/span&gt;((([Account].[All Members].[Account].&amp;amp;[5003],[Measures].[Value]) *
 ([Account].[All Members].[Account].&amp;amp;[5004],[Measures].[Value])) * 0.2)
&lt;span style="COLOR:blue;"&gt;SELECT NON EMPTY 
 &lt;/span&gt;([Account].[All Members].[Account].&amp;amp;[5005],
 [Measures].[Value], 
 &lt;span style="COLOR:maroon;"&gt;NonEmpty&lt;/span&gt;(({[Scenario].[All Members].[Scenario].&amp;amp;[1]},
 {&lt;span style="COLOR:blue;"&gt;Descendants&lt;/span&gt;([Time].[Monthly].[(All)].&amp;amp;[0], 1073741823, &lt;span style="COLOR:blue;"&gt;LEAVES&lt;/span&gt;)},
 {[Organisation].[Europe].[Level 02].&amp;amp;[11]}))) 
 &lt;span style="COLOR:blue;"&gt;properties &lt;/span&gt;[Scenario].[All Members].&lt;span style="COLOR:blue;"&gt;Key &lt;/span&gt;,
 [Time].[Monthly].&lt;span style="COLOR:blue;"&gt;Key &lt;/span&gt;,
 [Organisation].[Europe].&lt;span style="COLOR:blue;"&gt;Key &lt;/span&gt;,
 [Account].[All Members].&lt;span style="COLOR:blue;"&gt;Key 
ON COLUMNS 
FROM &lt;/span&gt;[Europe]&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;
&lt;p&gt;Here we can clearly tell (as marked in the above MDX code) how the first, second and third PEL arguments translate into MDX. If you find that the allocate statement is not quite doing what you want, then its quite useful to execute the MDX within SSMS to figure out exactly how the source model is being queried.&lt;/p&gt;
&lt;p&gt;Once you&amp;#39;re finished coding your business rule it can be executed via a scheduled job, manually, or via Excel.&lt;/p&gt;
&lt;p&gt;To be fair to Associations, you&amp;#39;re probably more likely to use them&amp;nbsp;for bulk transfer of data, perhaps to facilitate cross model&amp;nbsp;consolidation -&amp;nbsp;and they&amp;#39;re&amp;nbsp;extremely useful for this purpose.&amp;nbsp;The Allocate statement is perhaps more relevant for specific pieces of data that need to be adjusted in some way before being transferred to the destination. Despite this, the Allocate statement is the more flexible method and will allow you to support more&amp;nbsp;sophisticated cross model data transfer - if it’s needed.&lt;/p&gt;
&lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:71deb226-5f21-49c0-8576-2e6fd7f2ba57" style="PADDING-RIGHT:0px;DISPLAY:inline;PADDING-LEFT:0px;PADDING-BOTTOM:0px;MARGIN:0px;PADDING-TOP:0px;"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/PerformancePoint" rel="tag"&gt;PerformancePoint&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Allocate" rel="tag"&gt;Allocate&lt;/a&gt;,&lt;a href="http://technorati.com/tags/PEL" rel="tag"&gt;PEL&lt;/a&gt;,&lt;a href="http://technorati.com/tags/MDX" rel="tag"&gt;MDX&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=3382" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDX/default.aspx">MDX</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/PerformancePoint/default.aspx">PerformancePoint</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/PEL/default.aspx">PEL</category></item><item><title>MDX - 12 Months to Date</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/03/06/mdx-12-months-to-date.aspx</link><pubDate>Thu, 06 Mar 2008 17:02:00 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:3021</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=3021</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/03/06/mdx-12-months-to-date.aspx#comments</comments><description>&lt;p&gt;Analysis Services 2005 comes with the useful Add Business Intelligence wizard, which will assist you with numerous calculations, including 12 Months to Date. However, I tend to write such calculations myself, especially having found out that the 12 Months to Date MDX generated by the Business Intelligence wizard didn&amp;#39;t satisfy a client&amp;#39;s requirements. Although the calculation works fine at the Month level in a Date dimension, it doesn&amp;#39;t quite work at the day level - and it certainly doesn&amp;#39;t take leap years into account.&lt;/p&gt;
&lt;p&gt;The client&amp;#39;s requirement for day level 12 Months to Date was to return an accumulated view of the last 365 days back in the calendar hierarchy. However, for leap years, the calculation should take into account the 29th of February if the current year is a leap year.&lt;/p&gt;
&lt;p&gt;In order to get this to work, the first step is to modify the Date dimension so that it contains an attribute to signify whether the current day is affected by a leap year or not. In order to do this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1. Add a bit column to your date dimension table called AffectedByLeapYear (or similar) 
&lt;p&gt;2. Run a SQL Update statement to populate your new column: 
&lt;div&gt;&lt;pre class="code"&gt;&lt;span style="COLOR:blue;"&gt;UPDATE      &lt;/span&gt;dbo&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;DimTime
&lt;span style="COLOR:blue;"&gt;SET         &lt;/span&gt;AffectedByLeapYear &lt;span style="COLOR:gray;"&gt;= 
            &lt;/span&gt;&lt;span style="COLOR:blue;"&gt;CASE
                WHEN &lt;/span&gt;&lt;span style="COLOR:gray;"&gt;(&lt;/span&gt;CalendarYear &lt;span style="COLOR:gray;"&gt;% &lt;/span&gt;4 &lt;span style="COLOR:gray;"&gt;= &lt;/span&gt;0&lt;span style="COLOR:gray;"&gt;)  AND (&lt;/span&gt;CalendarYear &lt;span style="COLOR:gray;"&gt;% &lt;/span&gt;100 &lt;span style="COLOR:gray;"&gt;!= &lt;/span&gt;0 &lt;span style="COLOR:gray;"&gt;OR &lt;/span&gt;CalendarYear &lt;span style="COLOR:gray;"&gt;% &lt;/span&gt;400 &lt;span style="COLOR:gray;"&gt;= &lt;/span&gt;0&lt;span style="COLOR:gray;"&gt;)
                AND &lt;/span&gt;DayNumberOfYear &lt;span style="COLOR:gray;"&gt;&amp;gt;= &lt;/span&gt;60 &lt;span style="COLOR:blue;"&gt;THEN
                    &lt;/span&gt;1
                &lt;span style="COLOR:blue;"&gt;ELSE
                    &lt;/span&gt;0
            &lt;span style="COLOR:blue;"&gt;END &lt;/span&gt;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;3. Refresh your data source view&amp;nbsp; 
&lt;p&gt;4. Add a new attribute called &amp;#39;Affected By Leap Year&amp;#39; to your date dimension: 
&lt;p&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/MDX12MonthstoDate2_8C12/image_6.png"&gt;&lt;img style="BORDER-TOP-WIDTH:0px;BORDER-LEFT-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-RIGHT-WIDTH:0px;" height="244" alt="image" src="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/MDX12MonthstoDate2_8C12/image_thumb_2.png" width="168" border="0" /&gt;&lt;/a&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Now we’re ready to write the MDX for 12 Months to Date. In the MDX Script, the first step is to scope on a member called [Time Analysis].[Time Analysis].&amp;amp;[2], which is the 12 Months to Date member in my Time Utility dimension:&lt;/p&gt;
&lt;div&gt;&lt;pre class="code"&gt;&lt;span style="COLOR:blue;"&gt;Scope
&lt;/span&gt;(
    &lt;span style="COLOR:green;"&gt;//Scope on 12 months to date
    &lt;/span&gt;[Time Analysis].[Time Analysis].&amp;amp;[2]
    
) ;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Then we need a nested scope on the True member of the &amp;#39;Affected By Leap Year&amp;#39; attribute. This is really important because it means we can isolate the section of the cube that is affected by the leap year. Also, by using Scope(),&amp;nbsp; we can avoid complex IIf statements - everything is managed cleanly in the Scope() statement:&lt;/p&gt;
&lt;div&gt;&lt;pre class="code"&gt;&lt;span style="COLOR:blue;"&gt;Scope
&lt;/span&gt;(
    &lt;span style="COLOR:green;"&gt;//This statement is key - we scope on the cells that we know are affected by the leap year
    //This avoids a big and inefficient iif statement
    &lt;/span&gt;[Date].[Calendar].[Date].&lt;span style="COLOR:blue;"&gt;Members&lt;/span&gt;,
    [Date].[Affected By Leap Year].&amp;amp;[True]
) ;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we&amp;#39;re ready to perform the actual calculation. Seeing as this is for dates that are affected by a leap year, we need to aggregate a set with an extra member, meaning for any dates in a leap year beyond the 28th Feb, we will aggregate 366 days worth of data. Therefore, the following MDX statement is not too dissimilar to the kind of MDX that gets generated by the Business Intelligence wizard, aside from the fact its operating at the day level:&lt;/p&gt;
&lt;div&gt;&lt;pre class="code"&gt;&lt;span style="COLOR:blue;"&gt;This &lt;/span&gt;= &lt;span style="COLOR:maroon;"&gt;Aggregate
       &lt;/span&gt;(
            &lt;span style="COLOR:green;"&gt;//We need to go back an extra day here
            &lt;/span&gt;{[Time Analysis].[Time Analysis].&amp;amp;[1]} *
            &lt;span style="COLOR:maroon;"&gt;ParallelPeriod
            &lt;/span&gt;(
                [Date].[Calendar].[Date], 365, [Date].[Calendar].&lt;span style="COLOR:maroon;"&gt;CurrentMember
            &lt;/span&gt;)   : [Date].[Calendar].&lt;span style="COLOR:maroon;"&gt;CurrentMember
        &lt;/span&gt;) ;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;We then have a similar statement for the normal dates, which are any dates prior to the 29th Feb in a leap year, or any dates not in a leap year. The difference here is that the set that get aggregated just includes 365 days worth of data. When we put all these pieces together, we get:&lt;/p&gt;
&lt;div&gt;&lt;pre class="code"&gt;&lt;span style="COLOR:blue;"&gt;Scope
&lt;/span&gt;(
    &lt;span style="COLOR:green;"&gt;//Scope on 12 months to date
    &lt;/span&gt;[Time Analysis].[Time Analysis].&amp;amp;[2]
    
) ;
    &lt;span style="COLOR:blue;"&gt;Scope
    &lt;/span&gt;(
        &lt;span style="COLOR:green;"&gt;//This statement is key - we scope on the cells that we know are affected by the leap year
        //This avoids a big and inefficient iif statement
        &lt;/span&gt;[Date].[Calendar].[Date].&lt;span style="COLOR:blue;"&gt;Members&lt;/span&gt;,
        [Date].[Affected By Leap Year].&amp;amp;[True]
    ) ;
        &lt;span style="COLOR:blue;"&gt;This &lt;/span&gt;= &lt;span style="COLOR:maroon;"&gt;Aggregate
               &lt;/span&gt;(
                    &lt;span style="COLOR:green;"&gt;//We need to go back an extra day here
                    &lt;/span&gt;{[Time Analysis].[Time Analysis].&amp;amp;[1]} *
                    &lt;span style="COLOR:maroon;"&gt;ParallelPeriod
                    &lt;/span&gt;(
                        [Date].[Calendar].[Date], 365, [Date].[Calendar].&lt;span style="COLOR:maroon;"&gt;CurrentMember
                    &lt;/span&gt;)   : [Date].[Calendar].&lt;span style="COLOR:maroon;"&gt;CurrentMember
                &lt;/span&gt;) ;
    &lt;span style="COLOR:blue;"&gt;End Scope&lt;/span&gt;;

    &lt;span style="COLOR:blue;"&gt;Scope
    &lt;/span&gt;(
        &lt;span style="COLOR:green;"&gt;//Now we scope on the &amp;#39;normal&amp;#39; unaffected dates
        &lt;/span&gt;[Date].[Calendar].[Date].&lt;span style="COLOR:blue;"&gt;Members&lt;/span&gt;,
        [Date].[Affected By Leap Year].&amp;amp;[False]
    ) ;
        &lt;span style="COLOR:blue;"&gt;This &lt;/span&gt;= &lt;span style="COLOR:maroon;"&gt;Aggregate
               &lt;/span&gt;(
                    &lt;span style="COLOR:green;"&gt;//We are in a normal year (or in a leap before 29th Feb), just go back the 
                    //standard 365 days
                    &lt;/span&gt;{[Time Analysis].[Time Analysis].&amp;amp;[1]} *
                    &lt;span style="COLOR:maroon;"&gt;ParallelPeriod
                    &lt;/span&gt;(
                        [Date].[Calendar].[Date], 364, [Date].[Calendar].&lt;span style="COLOR:maroon;"&gt;CurrentMember
                    &lt;/span&gt;)   : [Date].[Calendar].&lt;span style="COLOR:maroon;"&gt;CurrentMember
                &lt;/span&gt;) ;
    &lt;span style="COLOR:blue;"&gt;End Scope&lt;/span&gt;;

&lt;span style="COLOR:blue;"&gt;End Scope &lt;/span&gt;;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;But we&amp;#39;re not finished yet! We now need deal with the other levels in the Calendar hierarchy, namely Half Years, Quarters, Months and Years. These are all dealt with by a relatively simple statement that sets the current member of the respective hierarchy to its last child. So for a month, we pick up the 12 Months Year to Date figure for the end of the month, for the Quarter we pick up the 12 Months Year to Date for the last month in the Quarter etc etc. For example:&lt;/p&gt;
&lt;div&gt;&lt;pre class="code"&gt;&lt;span style="COLOR:blue;"&gt;Scope
&lt;/span&gt;(
    [Date].[Calendar].[Month].&lt;span style="COLOR:blue;"&gt;Members
&lt;/span&gt;) ;
&lt;span style="COLOR:green;"&gt;//For months, always display data from the last day in the month
&lt;/span&gt;&lt;span style="COLOR:blue;"&gt;This &lt;/span&gt;= [Date].[Calendar].&lt;span style="COLOR:maroon;"&gt;CurrentMember&lt;/span&gt;.&lt;span style="COLOR:maroon;"&gt;LastChild&lt;/span&gt;;
&lt;span style="COLOR:blue;"&gt;End Scope&lt;/span&gt;;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Now you have a 12 Months to Date calculation that works at all levels in the Calendar hierarchy. Perhaps some of this is overkill for your requirements? I have spoken with users in the past who have only requested 12 Months to Date at the Month level - which is obviously a lot simpler. If you&amp;#39;re doing a 12 Months to Date calculation, then it almost goes without saying that you have to make it work at the Month level! But as for the other levels, such as Quarter, you may be ok just leaving these as NULL. Certainly the Time Intelligence wizard leaves Years, Half Years and Quarters out, just putting &amp;#39;N/A&amp;#39;. But all comes down to user requirements - and as we all know - you have to keep the users happy :-)&lt;/p&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=3021" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDX/default.aspx">MDX</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Analysis+Services/default.aspx">Analysis Services</category></item></channel></rss>