We can create a numeric ranking of the Customers by leveraging the MDX Rank function. The Rank function Continue reading The MDX Rank Function
All posts by Dustin Ryan
Creating Calculated Members with MDX
This week I got an email from a reader named Brad asking a specific question regarding MDX. Here is his question:
I need an MDX query to return me the top 5 values and then default all others to an “other” group and ranks based on the resulting measure value. For instance, if we are talking about a measure by month for a full year, I need the output to look like this:
Dim Measure Rank
Nov $500 1
Mar $400 2
Feb $300 3
Jan $200 4
Dec $100 5
Other $350 6Any input will be most appreciated!
Thanks,
Brad
I thought this was a good question which is why I thought I’d take the time to blog this out. In this example I’ll be using the Adventure Works cube and the Adventure Works Customer dimension.
The first step is to determine what are our Top 5 Customers. To do this, we can use a simple TopCount function to build a set of the Top 5 Customers.
with dynamic set [Top 5 Customers] as
Topcount(
[Customer].[Customer].[Customer].members,
5,
[Measures].[Internet Sales Amount]
)
Once we have our Top 5 Customers, then we can easily determine everyone else. To create our custom “Others” group, we need to create a Calculated Member (Custom Member). To create the Calculated Member, you need to specify the dimension, attribute hierarchy, and member name for your custom member (ie [Customer].[Customer].[Others]).
member [Customer].[Customer].[Others] as
Aggregate(
Except(
[Customer].[Customer].[Customer].members,
[Top 5 Customers]
)
)
To create our Others custom member, we need to use the Except function to basically specify that we want our custom member to include all our customers except the Top 5 Customers set we previously created. Also, don’t forget to wrap the set in the Aggregate function.
Lastly, we create our set that will actually be used in our query. This set will include our first set containing our Top 5 Customers as well as our Others custom member. We can use the Top 5 Customers set with our custom member because all members in the set are from the same Customers hierarchy.
dynamic set [Top 5 & Others] as
{
[Top 5 Customers],
[Customer].[Customer].[Others]
}
with dynamic set [Top 5 Customers] as
Topcount(
[Customer].[Customer].[Customer].members,
5,
[Measures].[Internet Sales Amount]
)
member [Customer].[Customer].[Others] as
Aggregate(
Except(
[Customer].[Customer].[Customer].members,
[Top 5 Customers]
)
)
dynamic set [Top 5 & Others] as
{
[Top 5 Customers],
[Customer].[Customer].[Others]
}
Select [Measures].[Internet Sales Amount] on 0,
[Top 5 & Others] on 1
From [Adventure Works]
I hope that was pretty straight forward and not too confusing. If you thought this was helpful leave me a comment and let me know! And if you have any questions or comments, feel free to leave a comment.
Thanks again, Brad, for the great question!
Check out my next blog post to see how to return the numeric rank of the customers!
How to Pass Multiple Values from an SSAS Report Drill Through Action to an SSRS Report
These past couple weeks I faced a client requirement to create an SSRS Report Action in an SSAS cube. This requirement dictated that a user should be able to browse a cube in an Excel pivot table and be able to drill through to an SSRS report (which used SQL Server as the data source) and view data at a monthly level. The twist was that if the user drilled from a year or quarter level, the appropriate months would be selected by default. I thought the solution I came up with was useful so here you have it, as recreated using Adventure Works 🙂 . I won’t walk through the step-by-step of creating a Report Action in SSAS, but I will hit the high points of overcoming this requirement.
I created the report action in the Cube Designer in SSDT and called it SSRS Sales Report. Because I want the users to be able to click on a measure, I set the Target type to “Cells” and the Target object to “All cells”.
But I really only want the users to see the action if they right-click the Reseller Sales Amount measure, so I added the following conditional logic so the action will only appear for the Reseller Sales Amount.
If you wanted to limit the action to appear beneath more than one measure but not all measures, just use “or” and specify other measures as seen in the example below. Also, in my specific requirement, the action should only work for a certain time frame (2007 forward in this example) so I added the logic you see after the “and”.
(Measures.CurrentMember is [Measures].[Reseller Sales Amount]
or Measures.CurrentMember is [Measures].[Internet Sales Amount])
and
COUNT(
Intersect(
{[Date].[Calendar Year].&[2007]:NULL},
[Date].[Calendar Year].CURRENTMEMBER)
) > 0
Next, I added in the Report Server URL and SSRS report link. In my case, the report server was SharePoint Integrated.
Now this is where things start to get interesting. I need to dynamically pass the year and month values to the SSRS report. What values to pass from SSAS really depends on the parameters in the SSRS report. If the SSRS report is using SSAS as a data source, then you may be able to use the unique name of the attribute members. In my case, the SSRS report used a regular ole SQL Server database as the data source so I needed to format the parameter values appropriately. The Parameter Name values should reflect the name of the parameters in the SSRS report. Also, if you’re using an SSRS report in SharePoint, don’t forget to include “rp:” as a prefix on the parameter name. For example, if your parameter is named “Year”, the parameter name you would enter into the action would be “rp:Year”. Again, this only applies if your SSRS report server is SharePoint integrated.
In the table below, you’ll noticed I’m using the Right function to return the right four characters of the name of the Calendar Year member. This is because in SSAS the Calendar Year caption appears as “CY 2007” for 2007 for example. In the SSRS report, however, the parameter value would be “2007”. The Month parameter is a little trickier. Because I want the user to be able to view multiple months at a time depending on what level of the Date Hierarchy they click on, I used the Generate function with the Descendants function to create a comma delimited string of the member values at the month level.
Parameter Name | Parameter Value |
rp:Year | RIGHT([Date].[Calendar Year].CURRENTMEMBER.NAME, 4) |
rp:Month |
GENERATE( DESCENDANTS([Date].[Calendar].CURRENTMEMBER, LEFT( 3), “&rp:Month=”) |
So if you’re wondering what the Generate function is doing here, take a look at the following query and the query results and that should clear it up for you.
with member [Measures].[Months param] as
GENERATE(
DESCENDANTS([Date].[Calendar].CURRENTMEMBER, [Date].[Calendar].[Month]),
LEFT([Date].[Calendar].CURRENTMEMBER.NAME,3),
"&rp:Month=")
Select [Measures].[Months param] on 0,
DESCENDANTS(
[Date].[Calendar].MEMBERS,
[Date].[Calendar].[Month],
SELF_AND_BEFORE
) on 1
From [Adventure Works]
In the results you can see that one of the neat things the Generate function can do is to create a delimited list. The third argument of the Generate function is my delimiter and in this case, I want it to be “&rp:Month=” so I can specify multiple values to pass to the parameter in the SSRS report. So if the user right-clicks a quarter, 3 months will be passed to the report. If the user clicks a semester, 6 months should be passed.
And here’s a screen cap of the Parameters in the Cube Design in SSDT in case you were wondering.
The last step is to make the caption for the action dynamic. Set the Caption is MDX to “True” and then you can do neat things with the action like use the CurrentMember’s Name property in the caption of the action.
Here’s the complete screen grab of the Action if you need it:
So that’s fantastic and all but what does it look like on the end user’s end? Here I have a pivot table with my date hierarchy and a couple measures. If I right-click the Reseller Sales Amount for Q4 2007, select Additional Actions, you’ll see the caption for my brand new action: View SSRS Sales Report for Q4 CY 2007.
And the report correctly picks up the values from the Action:
Keep in mind that this action works for any level of the Date Hierarchy down to the date level. Very slick!
If you look at the URL, you can see that the parameters being passed to the report through the URL query string:
http://spsqlbi/sites/BI%20Center%20Test/_layouts/15/ReportServer/RSViewerPage.aspx?rv:RelativeReportUrl=/sites/BI%20Center%20Test/Reporting%20Services/Adventure%20Works%20Reports/SalesReport.rdl&rp:Year=2007&rp:Month=Oct&rp:Month=Nov&rp:Month=Dec&rs:Command=Render&rs:Renderer=HTML5
Chances are that you’ll have to struggle through this a few times like I did. So here’s a couple tips to help you troubleshoot:
1. If the action does not appear where you expect it to in the pivot table, then there could be a syntax issue with your MDX in the Action definition in SSDT. Double-check your MDX.
2. If the action takes you to the report correctly, but the values in the parameters aren’t selected, then you probably aren’t passing the correct values to the report for the parameter. For example, if the Month parameter in the report uses Month Number as the values (ie 1 = Jan, 2 = Feb, etc) but you pass “January” from the Action, then the value will not be automatically selected.
3. Lastly, you are subject to the URL query string character limit in place by the browser. For example, IE has a limit of 2,083 characters last I checked, so if you’re trying to pass a selection of 500 customer values to an SSRS report you will probably run into some problems.
I hope that some of you out there found this useful. If you did, share it with a friend or leave me a comment and let me know! Thanks!
Script To Populate AdventureWorksDW DimDate
I do quite a bit of training for Pragmatic Works so I find myself working a lot with the AdventureWorksDW data warehouse database. AdventureWorksDW is a great test database if you’re wanting to demonstrate a concept or test a theory, but one of the things that has always bugged me is that the date dimension (dbo.DimDate) has holes in the data! Having a complete date dimension is important when working with SSAS. Well yesterday was the final straw, so I put together the following script that will fill in the missing dates in DimDate. You can specify a start date and end date and make the AdventureWorksDW DimDate date dimension as big as you like. Enjoy!
BEGIN TRAN declare @startdate date = '2005-01-01', @enddate date = '2014-12-31' IF @startdate IS NULL BEGIN Select Top 1 @startdate = FulldateAlternateKey From DimDate Order By DateKey ASC END Declare @datelist table (FullDate date) while @startdate <= @enddate Begin Insert into @datelist (FullDate) Select @startdate Set @startdate = dateadd(dd,1,@startdate) end Insert into dbo.DimDate (DateKey, FullDateAlternateKey, DayNumberOfWeek, EnglishDayNameOfWeek, SpanishDayNameOfWeek, FrenchDayNameOfWeek, DayNumberOfMonth, DayNumberOfYear, WeekNumberOfYear, EnglishMonthName, SpanishMonthName, FrenchMonthName, MonthNumberOfYear, CalendarQuarter, CalendarYear, CalendarSemester, FiscalQuarter, FiscalYear, FiscalSemester) select convert(int,convert(varchar,dl.FullDate,112)) as DateKey, dl.FullDate, datepart(dw,dl.FullDate) as DayNumberOfWeek, datename(weekday,dl.FullDate) as EnglishDayNameOfWeek, (Select top 1 SpanishDayNameOfWeek From DimDate Where EnglishDayNameOfWeek = datename(weekday,dl.FullDate)) as SpanishDayNameOfWeek, (Select top 1 FrenchDayNameOfWeek From DimDate Where EnglishDayNameOfWeek = datename(weekday,dl.FullDate)) as FrenchDayNameOfWeek, datepart(d,dl.FullDate) as DayNumberOfMonth, datepart(dy,dl.FullDate) as DayNumberOfYear, datepart(wk, dl.FUllDate) as WeekNumberOfYear, datename(MONTH,dl.FullDate) as EnglishMonthName, (Select top 1 SpanishMonthName From DimDate Where EnglishMonthName = datename(MONTH,dl.FullDate)) as SpanishMonthName, (Select top 1 FrenchMonthName From DimDate Where EnglishMonthName = datename(MONTH,dl.FullDate)) as FrenchMonthName, Month(dl.FullDate) as MonthNumberOfYear, datepart(qq, dl.FullDate) as CalendarQuarter, year(dl.FullDate) as CalendarYear, case datepart(qq, dl.FullDate) when 1 then 1 when 2 then 1 when 3 then 2 when 4 then 2 end as CalendarSemester, case datepart(qq, dl.FullDate) when 1 then 3 when 2 then 4 when 3 then 1 when 4 then 2 end as FiscalQuarter, case datepart(qq, dl.FullDate) when 1 then year(dl.FullDate) when 2 then year(dl.FullDate) when 3 then year(dl.FullDate) + 1 when 4 then year(dl.FullDate) + 1 end as FiscalYear, case datepart(qq, dl.FullDate) when 1 then 2 when 2 then 2 when 3 then 1 when 4 then 1 end as FiscalSemester from @datelist dl left join DimDate dd on dl.FullDate = dd.FullDateAlternateKey Where dd.FullDateAlternateKey is null COMMIT TRAN
How To Display 0’s Instead of NULL in your SSAS Cube & MDX Query Results
I’ve done a decent amount of SSAS and MDX query development over my career and one of the requirements that continually pops up is to display zeros instead of NULL when browsing the cube. Take the following query as an example:
Select {[Date].[Calendar Year].Children} on 0, NON EMPTY {[Geography].[State-Province].members} on 1 From [Adventure Works DW] Where [Measures].[Reseller Sales Amount]
And here are the results:
There are a couple ways display 0’s instead of NULL. The first way is to create logic in your MDX script that uses a CASE statement of an IIF expression that manually checks for empty cube space using the ISEMPTY function, similar to the following example:
SCOPE ([Measures].[Reseller Sales Amount]); THIS=IIF(ISEMPTY([Measures].[Reseller Sales Amount]),0,[Measures].[Reseller Sales Amount]); END SCOPE;
Pragmatic Works Release Doc xPress Server Edition
This month marks the official release of Pragmatic Works’ Doc xPress Server Edition! While everyone knows that Doc xPress gives you the capability to document your SQL Server databases, SSIS packages, SSAS cubes, and SSRS reports in way like no other tool. But with the release of the Server Edition, you can now host your documentation to a hosted web application making it now easier than ever to share documentation, lineage, and data dictionary information across your organization. Imagine being able to provide your technical users as well as your business users a one-stop-shop to all of your organization’s technical documentation without requiring anything to be installed on their desktop! Doc xPress Server Edition provides that capability!
Being able to view your Doc xPress-generated documentation online means that your technical team can quickly and easily assess the impact of changes in your BI environment by conducting a thorough lineage and impact analysis. What SSIS packages, SSRS reports, or SSAS cubes will be affected if I change a single column? Now you know!
How many times have you wondered what your environment looked like in the past? With Doc xPress, you can periodically snapshot your environment and compare snapshots over time so you can quickly and easily assess any changes that have occurred. And now you can view the documentation in your web browser without the need for any desktop configuration or installation.
If Doc xPress Server Edition sounds interesting and you’d like more information on the software, Pragmatic Works is presenting a free online webinar on the technology July 30th at 1:00 pm EST! Head over to PragmaticWorks.com (you’ll need to scroll down a screen or two) to get signed up and registered for the event.
Data Validation Via Data Surf
If you’re a developer like me, you’ve probably at some point had to validate data. Validation is often a tedious and boring job that can involve checking individual records and data values in multiple tables. It’s not fun but its a necessary part of our job after all. Recently I’ve discovered the Data Surf feature of DBA xPress which is part of the Pragmatic Workbench DBA toolset.
Data Surf enables me to begin browsing a database beginning with a single table or even a single record. Using that table or record as a start point, I can navigate to other tables related to my initial selection. I can specify that I’d like to view parent or child records of my selected row in a related table, which makes validating data a snap. So I’d like to show you how we can accomplish that.
I’ll begin by selecting the Data Surf feature from the Pragmatic Workbench.
Next I specify the SQL Server and Database I’d like to begin surfing. For this example I’ll start with my trust Adventure Works Data Warehouse.
Then I’ll select which table I’d like to begin my surf. Reseller Sales it is. You’ll notice if you hover your mouse over the name of the table, you can view the columns with their data types that exist in the table.
I can also optionally specify a specific search criteria. If I’d like to view certain records associated with a product, I can do that. This is a very nice feature for data validation.
Now I can see my surf’s starting point. I have a quick view of the records in the table as well as a row count.
If I click one of the records in the table , some options appear. On the left side of the screen, I can see related parent tables as well as child tables. In the case of the FactResellerSales, there happen to be no related child tables.
In the margin, if I click the dbo.DimEmployee table, I can now see the relationship between DimEmployee and FactResellerSales. I can also see the related Employee record based on the record I had initially highlighted in FactResellerSales. Because I had selected a record with an EmployeeKey of 285, when I click DimEmployee I see the record(s) with EmployeeKey 285. Also, by selecting the DimEmployee table, my related child and parent tables on the left have changed.
If I click on the DimSalesTerritory table, I can now see the relationship between DimSalesTerritory and DimEmployee. In the record viewer, I also see the DimSalesTerritory record(s) related to the DimEmployee record with EmployeeKey 285.
There’s also some other nice features available with Data Surf. I can customize the colors of the nodes in my diagram in order to produce a simple and easy to view ERD.
And once you’re done surfing, validating, etc, you can easily save your diagram as an image.
All in all, Data Surf is a very simple and easy to use tool that I can see as being very beneficial to the average developer. As a BI developer, I spend most of my time designing data warehouse solutions and having Data Surf in my back pocket is great. Download the free trial at PragmaticWorks.com and check out Data Surf.
SQL Saturday #298 Session Material Now Available (SSAS Tabular Models)
Another fantastic SQL Saturday event is in the books and it did not disappoint! There was a great crowd at University of North Florida in Jacksonville, FL and plenty of amazing sessions and speakers. Thank you to everyone that attended my session! I hope you enjoyed the session as much as I enjoyed presenting. It was a blast!
If you’re interested, the slide deck I used during the presentation is now available for your viewing pleasure, which you can see here.
If you have any questions or would like to contact me, you can always send me a message on Twitter or email me here. Again, thank you to all of my attendees!
Learn to Design Tabular Models at SQL Saturday #298 Jacksonville, FL on 5/10/2014
I’m excited to announce that on Saturday May 10th in a couple Saturdays I’ll be speaking at SQL Saturday 298 in Jacksonville, FL! SQL Saturday in Jacksonville always has an awesome turnout and I love speaking at this event! There will be tons of great free training sessions from the likes of Devin Knight, Robert Cain, Andy Warren, Adam Jorgensen, Max Trinidad, and tons of other great experts! If you’re in the North Florida area on May 10, you need to get registered for this event!
The session I’ll be delivering is called What the Tabular??? and will start at 1:15 PM EST on Saturday, May 10th! If you’re interested in learning how to build a Tabular Model, how to follow Tabular Model design best practices, and how to decide if a Tabular Model is the right solution to your business problem, then my session What the Tabular??? is for you! It’s a great beginner sessions and will feature tons of demos and walkthroughs of the technology!
So if you’re nearby, definitely get signed-up for this awesome event! You won’t regret it!
Do You Know Why Your MDX Query Is Slow?
Performance tuning MDX queries can often be a daunting and challenging task. But the first step in deciding where to begin your efforts to improve the performance of your query is to diagnose the source of the problem. There are two areas that could be causing our performance issue: 1. The design of our SSAS solution or 2. The design of our query. We need to figure out if we’ve written a bad query or designed a bad cube :).
How Do We Test The Query?
Usually an issue is discovered when a user comes to the BI team with a report that appears to be running slowly. So for our example, I have a pivot table in an excel workbook that is running a little slow. Let’s walk through this together and diagnose what the problem could be. Below here you can see the pivot table.
The first thing you’ll need to do is test the query and the best way to do this is to execute the query in isolation so that we can eliminate outside factors as part of the problem. So in this example I would execute the report outside of business hours. I want to make sure I do this outside of peak use time because one of the things I’ll need to do is clear the cache. In order to prevent SSAS from satisfying our query by leveraging the formula cache and the storage engine cache, we need to execute a ClearCache command to prevent our results from being corrupted. To do this, I’ll execute the following XMLA script in SSMS.
<ClearCache xmlns=”http://schemas.microsoft.com/analysisservices/2003/engine”> <Object> <DatabaseID>AdventureWorksDW2012MutliDimensional-EE</DatabaseID> <CubeID>Adventure Works</CubeID> </Object> </ClearCache>
Also, I’ll execute the following MDX to initialize my cube’s MDX script.
Select {} on 0 From [Adventure Works]
For us to figure out how long our query is taking to execute, we’re going to fire up SQL Server Profiler and execute a trace against SSAS while we execute the query so we can gather all the nitty gritty details of our query execution. By running a trace, we will be able to see all kinds of really helpful details like the total duration of the query, the partitions being queries, if aggregations are being used to answer the query, which attributes are being used, and much more.
To begin our trace, go to Tool > SQL Server Profilers in SSMS.
Flip the Server type to Analysis Services and set the Server name to your SSAS instance that is home to the SSAS database your report is pointed at.
Next you’ll see the Trace Properties window. Go to the Events Selection tab, and check the check box near the bottom right of the window to Show all events. Scroll down a little more than half way and find the events “Get Data From Aggregation” and “Query Subcube Verbose.” The “Get Data From Aggregation” event is fire when an aggregation is used to satisfy a query. This event is also especially helpful when trying to determine if the aggregations you have designed are actually useful. The “Query Subcube Verbose” event will give you very detailed information on which members from which dimension attributes are being queried to satisfy the query. Click Run when you’re done.
Now that the trace is running, its time to conduct our test.
1. The first thing I’ll do is execute the Clear Cache command.
<ClearCache xmlns=”http://schemas.microsoft.com/analysisservices/2003/engine”> <Object> <DatabaseID>AdventureWorksDW2012MutliDimensional-EE</DatabaseID> <CubeID>Adventure Works</CubeID> </Object> </ClearCache>
2. Initialize the Calculation script in your cube.
Select {} on 0 From [Adventure Works]
The report may take a few seconds or minutes to run depending on the query, but it should probably take longer than you’re used to simply because the query is running against an empty cache and SSAS will have to retrieve all of the data from storage.
When the query is finished executing, pause your Trace in SQL Server Profiler by clicking the pause button at the top of the Trace window. Now its time to take a look at the results. Find the Query End event in the Trace results. Find the Duration column. This number displays the total query duration in milliseconds. In my example, my query took just over 13 seconds to execute. So its not unbelievably slow, but certainly slower than we’d like.
In order to take a more in depth look at our query’s performance, lets save the Trace results to a SQL Server table so we can query it. Go to File > Save As > Trace Table.
Specify where you’d like to save the results and click OK.
Below you’ll see a very useful query that will break down where your query is experiencing a slow down. If you’re using this query for your testing, don’t forget to alter the From clause to query your trace table.
SELECT x.ConnectionID, x.QueryID, x.QueryDuration, p.SEDuration, CASE WHEN p.SEDuration > x.QueryDuration THEN NULL ELSE x.QueryDuration - p.SEDuration END AS FEDuration, y.[Number of SE Queries], y.[Thread Duration of SE Queries], w.[Aggregations Read], x.TextData FROM (SELECT a.ConnectionID, a.Duration AS QueryDuration, a.TextData, CAST (HashBytes('SHA1', CAST (reverse(CAST (TextData AS VARCHAR (MAX))) AS NVARCHAR (4000))) AS INT) AS QueryID FROM MyTraceTable AS a WHERE a.EventClass = 10) AS x -- Query End Event LEFT OUTER JOIN (/* Determine Query Subcube Verbose of Non-cache data */ SELECT ConnectionID, COUNT(*) AS [Number of SE Queries], SUM(Duration) AS [Thread Duration of SE Queries] FROM MyTraceTable WHERE EventClass = 12 -- Query Subcube Verbose AND EventSubclass = 22 -- Non-cache data GROUP BY ConnectionID) AS y ON y.ConnectionID = x.ConnectionID LEFT OUTER JOIN (/* Determine Aggregations that are ready from */ SELECT ConnectionID, COUNT(*) AS [Aggregations Read] FROM MyTraceTable WHERE EventClass = 60 -- Read from Aggregations GROUP BY ConnectionID) AS w ON w.ConnectionID = x.ConnectionID LEFT OUTER JOIN (/* Determine SE time */ SELECT ConnectionID, SUM(Duration) AS SEDuration FROM MyTraceTable WHERE EventClass = 11 -- Query SubCube GROUP BY ConnectionID) AS p ON p.ConnectionID = x.ConnectionID;
The results here are very telling. The column “QueryDuration” shows us the total execution time of the query. The column “SEDuration” shows us the amount of time SSAS spent pulling the data from storage (Storage Engine). The column “FEDuration” shows how long SSAS spent calculating our queries results (Formula Engine).
In this particular example, the vast majority of our query’s execution time is spent in the Formula Engine. Of the 13+ seconds spent executing the query, the query spends more than 12 seconds in the Formula Engine and only 297 milliseconds pulling the data from storage. This tells us that the problem is probably not with the design our SSAS solution, but rather a poorly written query. Unfortunately, this being an MDX query generated by Excel, there’s not a lot we can do about altering the query.
How Do We Fix The Query?
Typically when deciding where to spend your performance tuning efforts you want to start in the area where your query spends more than 30% of its time (If its a 50/50 split make an educated decision). In the previous example, we’ve determined our problem is with the query.
What can I do to improve my MDX query?
If you determine your query’s problem is the query itself, ensure SSAS is utilizing subspace computation instead of cell by cell computation. SSAS will usually evaluate groups of cells in your cube at a time but in certain situations SSAS will evaluate cube space one cell at a time. We want to to avoid that. You can get a hint that SSAS is calculation your results one cell at a time if the query on our trace table shows a large amount of Storage Engine queries. Certain SSAS/MDX functions can disable subspace computation.
1. Late binding functions (ex. StrtoMember, StrtoSet functions)
2. Set aliases
3. LookupCube function
Also, check out this blog for more info on ways to improve your MDX query.
But what if the problem is my SSAS solution’s design?
If you conduct your test and determine the majority of the query duration is spent pulling the data from storage, there’s a lot to consider when discussing cube design best practices. But here are some brief highlights of things to consider.
1. Can we design aggregations to help our query? Look at your test results and see if aggregations are being used to satisfy the query.
2. Can we implement a partition design strategy that keeps SSAS from having to query larger partitions?
3. Are the right partitions being queried? For example, if your query is asking for data for 2010 and you notice in your trace that the partitions for all the other years are being queried, this could indicate that SSAS is having a hard time figuring out which partition has the correct data. You may need to set the Slice property on your partition.
4. Create Attribute relationships
5. Leverage Natural Hierarchies
There are many more best practices for cube design, but this is probably a good starting point.
If you’ve found this helpful, share it with a colleague or a friend and leave me a comment. Feel free to leave me a question or feedback in the comments here or send me a note on Twitter! I love discussing new ideas and learning so don’t hesitate!