Sacha Tomey's blog

Molding the Microsoft BI Stack

MDX Stored Procedures (Part 2)

What seems like a lifetime ago I posted about MDX Stored Procedures and how we've built a helper class that provides a wrapper to an MDX query.

The helper class has been stable for quite sometime now and a few people have been asking me when I'm going to release it.  Well, today is the day.

You can download a Visual Studio 2005 C# Quick Start project from here.

The idea behind the class is that text files containing parameterised MDX queries can be executed against a cube with just a couple of lines of code.

Here are some notes to complement the quick start to get you going:

Step 1:  Create an instance of the helper class

MDXHelper helper = new MDXHelper
    ("localhost",
    "Adventure Works DW Standard Edition",
    "Adventure Works",
    MDXHelper.MDXResourceFormat.RelativeURL,
    "MDX/Product Category List.txt");

There are a couple of overloads on the constructor.  The one above specifies all the options.  The first three parameters set the Analysis Services Server, the database, the cube.  The last two parameters specify what format the MDX query.  In this case it's a Relative URL meaning that the next parameter must specify a relative URL pointing to a text file containing the desired query.  The alternative to a Relative URL is 'Text' which means the last parameter must be the query itself rather than a path to a file.

Step 2:  Add the parameters to the parameter collection.

helper.Parameters.Add(
    "@ProductCategory",
    CategoryList.SelectedValue);

Parameters are simply name/value pairs and are held internally as a generic dictionary<string,object>.  Parameters are optional but you will get an error if you invoke a parameterised query without defining a value for every parameter.

Step 3: Execute the Query

DataTable categoryList = helper.ExecuteDataTable();

A DataTable or a CellSet can be returned, again various overloads exist to provide additional flexibility, for example you can set the resource format and query path on the ExecuteDataTable e.g.

DataTable result = helper.ExecuteDataTable(MDXHelper.MDXResourceFormat.RelativeURL, "MDX/Internet Sales by Country.txt");

Additional Notes

The query definitions are cached in the ASP.NET application cache so if you make any changes to the query definition files you'll need to recycle the application pool.

The helper class will only work for Web Projects.  There is no support for WInForms/SmartClient deployments.

If you are returning a data table you can specify some additional options. 

  • Untyped is the fastest and default, returning a plain data table based on a flattened CellSet object. 
  • Typed brings back a Data Table with the Schema column data types set to match the CellSet object it was derived from.  This can be slow for large datasets.
  • UntypedFormatted brings back a plain data table but using the formatting e.g currency formats etc held in the cube.

There are some additional properties containing meta data debugging information that have been useful.  ExecutedMdxQuery contains the actual MDX that was executed after the parameter replacements have occurred.  DatabaseServerVersion, CubeDescription, CubeLastProcess and CubeLastSchemaUpdate are pulled from the server when a query is executed.

You can use parameters to define any aspect of the query.  E.g.  You could use parameters to swap rows and columns, define measures, pretty much anything you can achieve with search and replace.  On one hand this provides no end of flexibility but on the other provides diddly squat compile time debugging!

The same database connection is maintained between query execution calls.

All feedback gratefully received.

Comments

Greg Galloway said:

Interesting ideas and code. Thanks for sharing.

One comment... you've got three DataTableSchemaOptions options. Untyped uses the AdodbCommand.ExecuteReader as I would expect. However, the other two get into parsing XML. I think this is unnecessary. Try doing the following for those other two types and see if you can't use a data reader for all 3:

AdomdCommand cmd = new AdomdCommand();

cmd.Properties.Add("ReturnCellProperties", true);

More info here...

www.artisconsulting.com/.../Default.aspx

# November 21, 2007 4:16 PM

sachatomey said:

Thanks for the tip Greg.  However, I'm not so sure it will work for the Typed option, I couldn't see a CellProperty for the actual cell data type (I'd love to be proven wrong!) hence the need for the XML parsing - the UntypedFormatted option just jumps on the parsing bandwagon.

In our scenario, the Typed option is only used for binding our MDX sourced DataTables to a ChartFX object.  By default, the datatable created contains a bunch of string based columns that do not bind to a chart series.

Having said all that, I will certainly use your idea to optimise the UntypedFormatted option and save on the parsing overhead.

# November 21, 2007 5:20 PM

Greg Galloway said:

If you look at the datatype for the whole column (i.e. reader.GetFieldType) then you're correct that it says the type is System.Object. But that's just because that CalculatedMeasureA could end up being a string on one row and a decimal on the next. So you'll need to look at the datatype of each cell like this:

reader[i].GetType()

Will that work for you?

# November 21, 2007 10:29 PM

Tim Kent's blog said:

So the Adatis dev team have been busy ! As I mentioned in a previous post , one of our clients wanted

# November 22, 2007 11:45 PM

sachatomey said:

Hi Greg,

Yep - You are right, it looks like it will work for me!  Thank you.

That will certainly optimise the Typed and UntypedFormatted option and remove a whole raft of unneccessary bulk.

I'm kicking myself as I should have spotted that - I think I was sent down the wrong path purely by the existence of GetFieldType method !

As soon as I'm back on this project I'll update the class and associated download.

Thanks again - much appreciated !

# November 26, 2007 9:00 AM

MDX Stored Procedures (Part 1) - Sacha Tomey's blog said:

Pingback from  MDX Stored Procedures (Part 1) - Sacha Tomey&#39;s blog

# April 10, 2008 9:18 AM