September 15, 2013

Profile MDX & SQL queries from SSRS

 

Disclaimer : I have published the article in the TechNet Wiki as well. http://social.technet.microsoft.com/wiki/contents/articles/19980.profile-mdx-and-sql-queries-from-ssrs.aspx

One of the challenging aspect of integrating MDX queries with SSRS is debugging.  This is mainly because SSRS ignores the #Error messages and simply returns the empty cells. Another area of concern is parameterisation and query building. If you are using parameters for the MDX queries then even a simple mistake would be very hard to analyse without seeing the actual final query.

E.g.

Check the below query I am trying to display all the descendants set in column axis and resulted in error.

WITH MEMBER MEASURES.AXISText AS
 
Descendants([Geography].[Geography].currentmember,3,self_and_before)
 
SELECT {[Measures].[Internet Sales Amount], MEASURES.AXISText} ON COLUMNS
 
     ,Descendants([Geography].[Geography].[Country].&[Australia]
 
                   ,[Geography].[Geography].[State Province],self_and_after)DIMENSION PROPERTIES MEMBER_CAPTION, MEMBER_UNIQUE_NAME on Rows
 
FROM [Adventure Works]
 
CELL PROPERTIES VALUE,
 
                              BACK_COLOR
 
                            , FORE_COLOR
 
                            , FORMATTED_VALUE
 
                            , FORMAT_STRING
 
                            , FONT_NAME
 
                            , FONT_SIZE
 
                            , FONT_FLAGS
 

SQL server management studio Output sample:


I built a simple report on top of it and got the below result. As you can see the “#Error” has been replaced by empty cells. This is confusing isn’t it?


So knowing the direct SQL would have easily highlighted the issue

Now in order to easily trace the query from SSRS follow the below steps


         1. Create a common Template.




            2. Select the "Query End Event" as mentioned below




                3. Create a new trace and make sure to use the recently created template




                    4. Now this will trace both SSAS main Data set query and Parameter query from the SSRS in the computed form


                  E.g. For the Parameter


                  E.g. For the Main Dataset query


                  Include Filters

                  In real life the server will host multiple databases and even development server will get accessed by multiple team members. So it is better to customise your template based on the filters to focus on the query of your interest.




                  Best candidate for column filters


                  • Database name

                    • If you have multiple SSAS database then use this filter to narrow it down

                  • NTUsername

                    • If you using windows login or trying to access from Microsoft visual Studio then this would be your name 


                  • Success flag

                    • This is one of my favourite. If you are receiving an error in the SSRS window and you want to investigate it. Then please set a filter to “Success like 0”


                  Tracing queries in the SQL server database instance

                  Select the below events for tracing SSRS queries against SQL server instance



                  Note: Irrespective of using the Stored procedure from SSRS, RPC completed should be chosen

                  If you are connecting to SQL database engine instead of SSAS instance then you must use the filter to reduce the number of traces.
                  Best filter candidate


                  • Application Name

                    • If you are debugging a solution which is connecting to remote server then use “.Net SqlClient Data Provider” as your filter against Application name

                  • Database name

                    • Use the name of the database against which the query has been issued

                  • NTUsername

                    • If you are using windows login or trying to access from Microsoft visual Studio then this would be your name

                  August 25, 2013

                  Tabular, Multidimensional and Powerpivot which one to go for

                   

                  I came across this wonderful article in the MSDN . Between power pivot and Multidimensional the choice would be quite simple because the moment we want to build large scale DW the ideal choice would be Multidimensional SSAS cube.

                  Due to the introduction of Tabular model in 2012 and it’s simplicity most of us want to move towards Tabular. I can even see lot of queries related to tabular model in the forum as most of the Mid cab and small cab companies are choosing the Tabular models now. But I would definitely suggest checking the below article as not all of them supports all the feature. The best part of this article is feature comparison.

                  Before making the model decision please check this Article.

                  http://technet.microsoft.com/en-us/library/hh212940.aspx

                  Happy Reading Smile

                  August 18, 2013

                  Exist vs. Existing and Auto Exist–With Measures–Part 3

                  Please read the other two parts for continuity

                  Exist–With Measures

                  Most of the examples around the Existing keyword talks about the effect of Exists on Dimension . But what will happen if i use it against measure

                  Will that make any difference?

                  Existing keyword set the current context on measure calculation which is really invaluable

                  Let us check the below query

                  with member SalesAmtAustraliaCanada as
                  Aggregate({[Geography].[Geography].[Country].&[Australia],
                  [Geography].[Geography].[Country].&[Canada]
                  }
                  ,[Measures].[Reseller Sales Amount]
                  )
                  select {[Measures].[Reseller Sales Amount],SalesAmtAustraliaCanada} on 0,
                  [Date].[Calendar].[Calendar Year].members on 1
                  from [Adventure Works]


                  image



                  As expected, the Calculated measure has summed up the sales related to Australia and Canada, which is way lower than total amount against a year.



                  Let us execute the below query and get the sales against different countries

                  with member SalesAmtAustraliaCanada as
                  Aggregate({[Geography].[Geography].[Country].&[Australia],
                  [Geography].[Geography].[Country].&[Canada]
                  }
                  ,[Measures].[Reseller Sales Amount]
                  )
                  select {[Measures].[Reseller Sales Amount],SalesAmtAustraliaCanada} on 0,
                  [Geography].[Geography].[Country].members on 1
                  from [Adventure Works]
                  image

                  As you can see, it displays the summed value against each country and they are same. Though theoretically this is correct, we would ideally want to get the data only against Australia and Canada for this new measure.


                  So what is happening here?


                  Calculated members doesn’t get affected by the current query context . If we need the current context then we need existing keyword

                  with member SalesAmtAustraliaCanada as
                  Aggregate(existing {[Geography].[Geography].[Country].&[Australia],
                  [Geography].[Geography].[Country].&[Canada]
                  }
                  ,[Measures].[Reseller Sales Amount]
                  )
                  select {[Measures].[Reseller Sales Amount],SalesAmtAustraliaCanada} on 0,
                  [Geography].[Geography].[Country].members on 1
                  from [Adventure Works]


                  image



                  What will happen if we  change the query back to Date dimension?

                  with member SalesAmtAustraliaCanada as
                  Aggregate(existing {[Geography].[Geography].[Country].&[Australia],
                  [Geography].[Geography].[Country].&[Canada]
                  }
                  ,[Measures].[Reseller Sales Amount]
                  )
                  select {[Measures].[Reseller Sales Amount],SalesAmtAustraliaCanada} on 0,
                  [Date].[Calendar].[Calendar Year].members on 1
                  from [Adventure Works]


                  image



                  It produces the same result as above because in the above query the current context for Geography dimension is “All Geographies”



                  let us confirm this with below query

                  with member SalesAmtAustraliaCanada as
                  Aggregate(existing {[Geography].[Geography].[Country].&[Australia],
                  [Geography].[Geography].[Country].&[Canada]
                  }
                  ,[Measures].[Reseller Sales Amount]
                  )
                  member Geographycurrenthier as
                  [Geography].[Geography].membervalue
                  select {[Measures].[Reseller Sales Amount],SalesAmtAustraliaCanada,Geographycurrenthier} on 0,
                  [Date].[Calendar].[Calendar Year].members on 1
                  from [Adventure Works]


                  image



                  This is very useful technique for End user reporting.



                  Happy reading Smile

                  July 14, 2013

                  Exist v.s Existing and Auto Exist in MDX – Part 2–Usual confusion

                  Please read the Part1 of the same series to get the continuity

                  Usual confusions

                  • Exists and “Auto exists” are nearly same only difference is Exists function don’t display the second set .

                  Actually no. One set the context other doesn’t

                  SELECT [Measures].[Reseller Sales Amount] ON 0           
                  , Exists( [Product].[Subcategory].[Subcategory].Members
                  ,[Product].[Category].[Clothing]
                  )on 1
                  FROM [Adventure Works]
                  where [Product].[Category].[Bikes]

                  Above query will produce no results because we are slicing by two different members(Bikes & Clothing) at the same time. But the the query allows you to use the same dimension hierarchy in both rows Axis and slicer axis.


                  But the below query will produce errors, because it sets the context


                  SELECT [Measures].[Reseller Sales Amount] ON 0      
                  , [Product].[Subcategory].[Subcategory].Members *
                  [Product].[Category].[Clothing]

                  on 1
                  FROM [Adventure Works]
                  where [Product].[Category].&[1]

                  image_thumb[22]



                  • Existing keyword is just an extension of Exists, we can achieve the similar results with exist and currentmember function



                  Yes, But use of existing will improve the maintainability of code


                  Both the below codes will produce the same results


                  with member countofsubcategory as 
                  count( existing [Product].[Subcategory].[Subcategory].Members)
                  SELECT {[Measures].[Reseller Sales Amount], countofsubcategory} ON 0
                  , [Product].[Category].[Category].Members
                  on 1
                  from [Adventure Works]

                  with member countofsubcategory as
                  count( exists( [Product].[Subcategory].[Subcategory].Members
                  ,[Product].[Category].currentmember ))
                  SELECT {[Measures].[Reseller Sales Amount], countofsubcategory} ON 0
                  , [Product].[Category].[Category].Members
                  on 1
                  from [Adventure Works]
                  image



                  • DO we have not exists function ?



                  No. But this can be simulated with except function


                  The below query produces 8 rows of data


                  SELECT [Measures].[Reseller Sales Amount] ON 0      
                  , Exists( [Product].[Subcategory].[Subcategory].Members ,
                  [Product].[Category].[Clothing]
                  )

                  on 1
                  FROM [Adventure Works]
                  image 


                  With the except function in the below query it produces the opposite set with 30 rows 


                  SELECT [Measures].[Reseller Sales Amount] ON 0      
                  ,EXCEPT([Product].[Subcategory].[Subcategory].Members,
                  Exists( [Product].[Subcategory].[Subcategory].Members ,
                  [Product].[Category].[Clothing]
                  )
                  )
                  on 1
                  FROM [Adventure Works]

                  image_thumb[24]

                  image_thumb[26]



                  • Generate function forces it’s own context against existing keyword



                  Check the below query. It is clear that existing keyword uses the generate function’s context and that is why we got 3 subcategory against all category in the result set

                  with member countofsubcategory as 
                  Generate ([Product].[Category].[Bikes]
                  , count( existing [Product].[Subcategory].[Subcategory].Members)
                  )
                  SELECT {[Measures].[Reseller Sales Amount], countofsubcategory} ON 0
                  , [Product].[Category].[Category].Members
                  on 1
                  from [Adventure Works]

                  image_thumb[28]

                   

                  This is an another proof that existing keyword is simply an extension of Exists function

                  Happy Reading Smile

                  July 7, 2013

                  Exist v.s Existing and Auto Exist in MDX – Part 1

                  Though Exist, existing and Auto exist are conceptually very simple, they are the root cause for many confusion in the complex MDX queries. So let us do a simple comparisons

                   

                  Normal Behaviour

                  Auto Exists

                  Exists

                  Existing
                  Theory

                  When the two sets are cross joined it will produce all possible combination. E.g. if we join two sets with 4 dimension each then it will produce 16 members

                  It forces the natural hierarchy in the below Scenarios


                  When two sets containing members of the same dimension are cross-joined, the resulting set is limited to those combinations of members actually observed in that dimension.
                  When the WHERE
                  clause contains a member of a dimension, sets along the axes containing members from that same dimension are limited as well.

                  It forces the natural hierarchy with out returning the forcing/second set

                  Auto exist is not applicable to calculated members and if we want to force this behaviour then we need existing keyword

                  When to useWhen we want to display the measures based on two different dimensionWhen we want to display the measures based on the members in different level from same hierarchyAchieve the same Auto exists results with out displaying second DimensionForces the local context in the calculated members

                  Normal Behaviour

                  Run the below query

                  SELECT [Measures].[Reseller Sales Amount] ON 0
                  , [Product].[Category].[Category].Members on 1
                  from [Adventure Works]

                  image

                  Based on the above query we have 4 Categories in the Product Categories Hierarchy

                  SELECT [Measures].[Reseller Sales Amount] ON 0
                  , [Geography].[Geography].[Country].members
                  on 1
                  from [Adventure Works]
                  image

                  Based on the above query we have 6 countries under geography hierarchy

                  Let us Cross Join it

                  SELECT [Measures].[Reseller Sales Amount] ON 0
                  ,[Product].[Category].[Category].Members
                  * {[Geography].[Geography].[Country].Members
                  } on 1
                  from [Adventure Works]

                  image

                  image

                  So along with the header row it has returned 25 rows which is direct cross product of 4 Products and 6 countries (24 data row + 1 Header row)

                  What other ways to achieve the cross join

                  You can use the cross join function as shown below

                  SELECT [Measures].[Reseller Sales Amount] ON 0
                  ,crossjoin ([Product].[Category].[Category].Members
                  , [Geography].[Geography].[Country].Members
                  ) on 1
                  from [Adventure Works]

                  Even if you remove the Crossjoin keyword in the above query,  it will still produce the same result .

                   
                  SELECT [Measures].[Reseller Sales Amount] ON 0
                        ,([Product].[Category].[Category].Members
                  , [Geography].[Geography].[Country].Members
                           ) on 1
                  from [Adventure Works]
                  image
                   The reason is MDX will try to resolve the other dimensions of the cube for every combination (remember the partial tuple law)and will get you the cross join.

                  Auto Exists

                  In the above example we have used the cross join between two different hierarchies (Product.category and geography.country). let us join between category and sub category from same product hierarchy.

                  Step 1: Let us find the count of subcategory first

                  SELECT [Measures].[Reseller Sales Amount] ON 0
                  , [Product].[SubCategory].[SubCategory].Members on 1
                  from [Adventure Works]
                  image
                   Along with header row we have 38 subcategories, let us cross join subcategory and category.

                   

                  SELECT [Measures].[Reseller Sales Amount] ON 0
                  , [Product].[Category].[Category].Members *
                  [Product].[Subcategory].[Subcategory].Members on 1
                  from [Adventure Works]
                  image
                  image

                  As you can see from the output though it has given the cross product, it still only has the same 38 (37 rows of data + 1 header) rows in the output. as per the normal behaviour it should be 4*38 = 152

                  The reason is, based on the Product category hierarchy, only valid category and Subcategory combinations are produced. 

                   

                  What is Existing 

                  Let us create a calculated member in the above query
                  with member countofsubcategory as 
                  count( [Product].[Subcategory].[Subcategory].Members)
                  SELECT {[Measures].[Reseller Sales Amount], countofsubcategory} ON 0
                  , [Product].[Category].[Category].Members *
                  [Product].[Subcategory].[Subcategory].Members
                  on 1
                  from [Adventure Works]
                  image 
                  Though we are displaying each subcategory in the list, still we got the total count . This is because Auto exist doesn’t control the calculated members  
                   
                  So by inserting Existing keyword in the above query,it will produce the expected result which 1 per subcategory
                  with member countofsubcategory as 
                  count( existing [Product].[Subcategory].[Subcategory].Members)
                  SELECT {[Measures].[Reseller Sales Amount], countofsubcategory} ON 0
                  , [Product].[Category].[Category].Members *
                  [Product].[Subcategory].[Subcategory].Members
                  on 1
                  from [Adventure Works]
                  image

                   

                  What is Exists function ?

                  Exists uses the “Auto exist” functionality but avoid displaying the “second set” in the results

                  SELECT [Measures].[Reseller Sales Amount] ON 0      
                  , Exists( [Product].[Subcategory].[Subcategory].Members ,
                  [Product].[Category].[Clothing]
                  )
                  on 1
                  FROM [Adventure Works]

                  image

                  It only displays the subcategories which are related to Clothing category without displaying category in the result.

                  June 25, 2013

                  Step By step Guide to integrate SQL management studio with Subversion

                   
                  If you have worked in .net and migrated to database like me then you will surely miss the free add-ins for subversion (ankhsvn). As more and more companies are moving towards the open source versioning system like subversion, it is really painful that Microsoft SSMS studio doesn't have any add-in for subversion.
                   
                  There is a neat and simple solution available from Redgate but it requires license for each PC so if you are working in a place where purchase decisions are difficult to come by then this solution can be very handy.
                   
                  Required: TortoiseSVN in your local machine and write access to subversion repository
                   
                  Create or Check out an existing solution
                                                                 
                  ·        Decide and create a working folder in C: (You can use other drives but Avoid Network drives)
                  <!--[if !supportLists]-->o   <!--[endif]-->Ex.  “C:\Test Project”
                  ·         Create a folder in the Subversion repository 
                                 
                   

                  <!--[if !supportLists]-->·        Right click on the newly created folder and Integrate it with your work folder using the checkout option
                    

                  Once you checked in, your folder will appear like below
                   
                   
                  <!--[if !supportLists]-->                                                 Note: You can also check out an existing solution to your work folder
                   
                  ·        Create a project in the SQL management Studio and store the solution into above working directory (c:\testproject

                   
                   
                   
                  Create External Tools
                    Open the external tools window
                   
                   
                   

                  Create External Tools
                    Enter the title, command , Arguments and initial Directory as per the below list

                   
                   
                  List of commands and parameters
                   
                  Commit:
                   
                  Title : SVN CommitSollution
                  Command : C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe
                  Arguments : /command:commit /path:"$(SolutionDir)"
                  Intial Directory: $(SolutionDir)
                   
                  CommitFile:
                   
                  Title : SVN Commitfile
                  Command : C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe
                  Arguments : /command:commit /path:"$(ItemFileName)$(ItemExt) "
                  Intial Directory: $(ItemDir)
                   
                  Revision History
                   
                  Title : SVN-RevisionHistoryforItem
                  Command : C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe
                  Arguments : /command:log /path:"$(SolutionDir)"
                  Intial Directory: $(SolutionDir)
                   
                   
                  How To use ?
                   
                  Choose the projects and select CommitSollution from your tools
                   
                   


                   
                   
                  It will list out all the solution files. Please choose the new Project(s) which you would like to add.
                   
                  Note: You can use the All button to select all the files automatically
                   
                   



                  Commit changes of your files

                  If you prefer to commit the changes in a single file then “CommitFile” option can be used
                  <!--[if !supportLists]-->a.       <!--[endif]-->Select the file which has had some change from the last check in
                  <!--[if !supportLists]-->b.      <!--[endif]-->Only the file which has had any change will appear
                   


                   

                  Check the revision History

                  Another common requirement and advantage of using source control would be comparing it against one of the older versions.
                   
                  In my example I have two versions:
                  First one I checked in during the initial upload and the upload in the previous step.
                  In the second version I have added the below query
                  select * from [dbo].[DimEmployee]” into the sampleQuery
                   
                  If I select the samplequery.sql and select SVN-Revisiohistoryforitem then it will appear as below
                   

                   
                   
                  You can select the version of your choice and compare it with working copy.
                   




                  Just works like a charm J
                   

                  Thanks to John Rummell on external tools and SSMS
                   

                  May 12, 2013

                  Use SSMS 2012 with SQL server 2008




                  SQL server Management studio 2012 is freely downloadable from Microsoft site and it is compatible with Sql server 2008.I was using this for past 3 months and found it very useful, so I listed down some useful new features and changes

                  Even if your company is not ready to upgrade to 2012, this free client upgrade is worth the try

                  Some useful features

                  1. Usability : SSMS 2012 is powered by visual studio so the UI looks lot similar with Visual studio  
                  2. I always have issues with 2008 intellisense (autofill) which is very slow and sometimes even manual refresh take longer time to refresh the metadata. But 2012 is very impressive and i am much surprised about the meta data management.
                  3. In 2012 intellisense accept part name search.  E.g . If your table name is abc_xyz_ETL then you can type xyz which will pick all the tables which contains the xyz while writing the query
                  4. The new improved MDX query editor with MDX intelisense is very useful for SSAS query editing. 
                  5. New Source safe integration - the SSMS 2012 can be easily integrated with team foundation server with a free plugin. I am really hoping to see some free plugin for subversion soon. 
                  6. Projects are easy to create and maintain
                  7. You can create custom template which can be created and used across organisation
                  8. New SQL CMD Mode. So you can mix DOS and sql command in the script. Very useful to move and import from excel/csv files via DB links
                  9. Very easy to pass the list of servers to the new joiner as you can export the registered servers to other machine

                   

                  For more information please refer to