<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>The Frog-Blog &#187; Dynamic</title>
	<atom:link href="http://www.purplefrogsystems.com/blog/index.php/tag/dynamic/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.purplefrogsystems.com/blog</link>
	<description>Purple Frog-Blog for all that is Business Intelligence</description>
	<lastBuildDate>Mon, 06 Sep 2010 21:20:51 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Dynamic MDX in Reporting Services</title>
		<link>http://www.purplefrogsystems.com/blog/index.php/2008/09/dynamic-mdx-in-reporting-services/</link>
		<comments>http://www.purplefrogsystems.com/blog/index.php/2008/09/dynamic-mdx-in-reporting-services/#comments</comments>
		<pubDate>Thu, 18 Sep 2008 14:28:36 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Analysis Services]]></category>
		<category><![CDATA[Reporting Services]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Dynamic]]></category>
		<category><![CDATA[MDX]]></category>
		<category><![CDATA[OLAP]]></category>

		<guid isPermaLink="false">http://www.purplefrogsystems.com/blog/?p=18</guid>
		<description><![CDATA[After a couple of days off work last week with SQL Bits III and SQL 2008 Unleashed, it&#8217;s back to the grindstone this week; however before I get onto the serious stuff I&#8217;d like to say thank you to the organisers of both events. Watching you on stage headbanging to Rockstar &#8211; legendary! (You can [...]]]></description>
			<content:encoded><![CDATA[<p>After a couple of days off work last week with SQL Bits III and SQL 2008 Unleashed, it&#8217;s back to the grindstone this week; however before I get onto the serious stuff I&#8217;d like to say thank you to the organisers of both events. Watching you on stage headbanging to Rockstar &#8211; legendary! (You can see for yourself <a href="/images/blog/rockstar1.jpg" target="_blank">here</a> and <a href="/images/blog/rockstar2.jpg" target="_blank">here</a>&#8230;).</p>
<p>Anyway, back to MDX&#8230;</p>
<p>This post explains how you can build a dynamic MDX query in Reportins Services, customised to the users requirements. This can often bring some quite major performance benefits.</p>
<p>Lets say for example that you want to have a sales report grouped dynamically by either product, sales person, department or customer. Normally you would use a single static MDX query, and then add a dynamic grouping to the table in the report. This is fine, until you try it on a large dataset. If you only have 50 products, 2 salesmen, 5 departments and 100 customers, your MDX needs to return 50,000 records, the report then has to summarise all of this into the level of grouping you want. This renders the pre-calculated aggregations in OLAP pretty much worthless.</p>
<p>To get around this, you can generate your MDX dynamically, so that the query returns the data already grouped into the correct level. You can also use this to add extra filters to the query, but only when they are required.</p>
<p>To start with, lets see how we would do this normally with SQL. Assuming we&#8217;re working from a denormalised table such as this</p>
<table width="100%" align="center">
<tbody>
<tr>
<td align="center"><img src="/images/blog/DynamicMDXtable.png" alt="Dynamic MDX Table" /></td>
</tr>
</tbody>
</table>
<p>Dynamic SQL is pretty simple, instead of having your dataset query as</p>
<pre><span style="color: #008000;">  SELECT SalesPerson,
      Sum(Sales) AS Sales
   FROM tblData
   GROUP BY SalesPerson
</span></pre>
<p>you can add a report parameter called GroupBy,</p>
<table width="100%" align="center">
<tbody>
<tr>
<td align="center"><img src="/images/blog/DynamicMDXSQL.png" alt="Dynamic MDX SQL" /></td>
</tr>
</tbody>
</table>
<p>and then use an expression as your dataset</p>
<pre><span style="color: #008000;">  ="SELECT "
      + Parameters!GroupBy.Value + " AS GroupName,
      Sum(Sales) AS Sales
   FROM tblData
   GROUP BY " + Parameters!GroupBy.Value
</span></pre>
<p>However MDX queries don&#8217;t let you use an expression in the dataset, so we have to work around that quite major limitation. To do this we make use of the OpenRowset command. You need to enable it in the surface area config tool, but once it&#8217;s enabled you can fire off an OpenRowset command to SQL Server, which will then pass it on to the cube. As the datasource connnection is to SQL Server not Analysis Services, it allows you to use an expression in the dataset.</p>
<pre><span style="color: #008000;">  ="SELECT * FROM OpenRowset('MSOLAP',
    'DATA SOURCE=localhost; Initial Catalog=SalesTest;',
    'SELECT
      {[Measures].[Sales]} ON 0,
      NON EMPTY {[Product].[Product].[Product].Members} ON 1
      FROM Sales') "
</span></pre>
<p>You can then expand this to make it dynamic depending on the value of a parameter. Before we do this though, there are a couple of items I should point out.<br />
1) As the expression can get quite large, I find it much easier to create the query from a custom code function<br />
2) As SSRS can&#8217;t interpret the expression at runtime, you need to define the fields in your dataset manually (more on this later)</p>
<p>To use a custom code function, just change the dataset expression to</p>
<pre><span style="color: #008000;">  =Code.CreateMDX(Parameters)</span></pre>
<p>We pass in the parameters collection so that we can use the parameters to determine the query. Create a function called CreateMDX() in the code block</p>
<table width="100%" align="center">
<tbody>
<tr>
<td align="center"><img src="/images/blog/DynamicMDXCode.png" alt="Dynamic MDX Code" /></td>
</tr>
</tbody>
</table>
<p>You can then construct your MDX query within the code block.</p>
<pre><span style="color: #008000;">  Public Function CreateMDX(ByVal params as Parameters) as string

   Dim mdx as string 

   mdx = "SELECT * FROM OpenRowset("
   mdx += " 'MSOLAP', "
   mdx += " 'DATA SOURCE=localhost; Initial Catalog=SalesTest;', "
   mdx += " ' SELECT {[Measures].[Sales]} ON 0, "
   mdx += "    NON EMPTY {[Product].[Product].[Product].Members} ON 1 "
   mdx += "   FROM Sales ' "
   mdx += ")"

   return mdx

End Function
</span></pre>
<p>We&#8217;re almost there&#8230;<br />
The next problem is that the field names returned by the query are less than helpful. To fix this we just need to alias the fields in the query. I usually take the opportunity of casting the numerical fields so that the report treats them as such, rather than as a string.</p>
<pre><span style="color: #008000;">  Public Function CreateMDX(ByVal params as Parameters)
                  as string

   Dim mdx as string 

   mdx = "SELECT "
   mdx += "  ""[Product].[Product].[Product].[MEMBER_CAPTION]"" AS GroupName, "
   mdx += "   Cast(""[Measures].[Sales]"" AS int) AS Sales "
   mdx += " FROM OpenRowset("
   mdx += " 'MSOLAP', "
   mdx += " 'DATA SOURCE=localhost; Initial Catalog=SalesTest;', "
   mdx += " ' SELECT {[Measures].[Sales]} ON 0, "
   mdx += "    NON EMPTY {[Product].[Product].[Product].Members} ON 1 "
   mdx += "   FROM Sales ' "
   mdx += ")"

   return mdx

End Function
</span></pre>
<p>(please do watch out for the quotes, double quotes and double double quotes, it can get a little confusing!)<br />
We then need to tell the dataset which fields to expect from the query.</p>
<table width="100%" align="center">
<tbody>
<tr>
<td align="center"><img src="/images/blog/DynamicMDXFields.png" alt="Dynamic MDX Fields" /></td>
</tr>
</tbody>
</table>
<p>You can now use the dataset in your report.<br />
However, the original point of this was to make the query dynamic&#8230; All we need to do to achieve this is expand the VB.Net code accordingly.</p>
<pre><span style="color: #008000;">  Public Function CreateMDX(ByVal params as Parameters) as string

   Dim mdx as string 

   mdx = "SELECT "

IF params("GroupBy").Value.ToString()="Product" THEN
   mdx += "  ""[Product].[Product].[Product]"
ELSE IF params("GroupBy").Value.ToString()="SalesPerson" THEN
   mdx += "  ""[Sales Person].[Sales Person].[Sales Person]"
ELSE IF params("GroupBy").Value.ToString()="Customer" THEN
   mdx += "  ""[Customer].[Customer].[Customer]"
END IF

   mdx += ".[MEMBER_CAPTION]"" AS GroupName, " 

   mdx += "   Cast(""[Measures].[Sales]"" AS int) AS Sales "
   mdx += " FROM OpenRowset("
   mdx += " 'MSOLAP', "
   mdx += " 'DATA SOURCE=localhost; Initial Catalog=SalesTest;', "
   mdx += " ' SELECT {[Measures].[Sales]} ON 0, "

IF params("GroupBy").Value.ToString()="Product" THEN
   mdx += "  NON EMPTY {[Product].[Product].[Product]"
ELSE IF params("GroupBy").Value.ToString()="SalesPerson" THEN
   mdx += "  NON EMPTY {[Sales Person].[Sales Person].[Sales Person]"
ELSE IF params("GroupBy").Value.ToString()="Customer" THEN
   mdx += "  NON EMPTY {[Customer].[Customer].[Customer]"
END IF

   mdx += ".Members} ON 1 "

   mdx += "   FROM Sales ' "
   mdx += ")"

   return mdx

End Function
</span></pre>
<p>It&#8217;s certainly not that simple, and debugging can cause a few headaches, but you can benefit from a massive performance in complex reports if you&#8217;re prepared to put the work in.</p>
<table width="100%" align="center">
<tbody>
<tr>
<td align="center"><img src="/images/blog/DynamicMDXResults.png" alt="Dynamic MDX Results" /></td>
</tr>
</tbody>
</table>
<p>You can download the project files <a href="/Download/blog/DynamicMDX.zip"><strong>here</strong></a></p>
<p>As always, please let me know how you get on with it, and shout if you have any queries&#8230;</p>
<p>Alex</p>
]]></content:encoded>
			<wfw:commentRss>http://www.purplefrogsystems.com/blog/index.php/2008/09/dynamic-mdx-in-reporting-services/feed/</wfw:commentRss>
		<slash:comments>32</slash:comments>
		</item>
	</channel>
</rss>
