Ramunas Balukonis Blog

Ramunas Balukonis Blog

November 21st, 2007

Creating roles from Active Directory grups

This script could help Analysis Services administrators to load and configure big amount of roles. Supose,  I have to configure  100+ roles for every shop in you enterprise. Every shop have his own group in Active Directory:    active Directory adminsitrator already created AD groups for shops with description “new”.  In AS database I have store dimension and “stor bkey” attribute for each shop.
My task is loop via Active directory groups, look for group description “new”, then create role in Analysis Services, add  AD group  as role member, grant read permission on cube, define allowed set to his own shop, default member and Visual Totals. After I need to delete description “new”.
1′st part of script  looping via AD groups in container and look for description “new” and then call procedure CreateRole.  

Public Sub Main()
Dim ADContainer As System.DirectoryServices.DirectoryEntry = _
New System.DirectoryServices.DirectoryEntry(LDAP://OU=MyGruops3,OU=MyGruops2,OU=MyGruops1,DC=MyEnterprise,DC=int)
Dim ADGroup As System.DirectoryServices.DirectoryEntry
Dim sGroupName As String
For Each ADGroup In ADContainer.Children
If ADGroup.Properties(“Description”).Value = “new” Then
sGroupName = ADGroup.Properties(“cn”).Value
CreateRole(sGroupName)
ADGroup.Properties(
“Description”).RemoveAt(0)
ADGroup.CommitChanges()
End If
Next
Dts.TaskResult = Dts.Results.Success
End Sub

Rest of script – configuring role:

Private Sub CreateRole(ByVal sGroupName As String)
Dim RoleMember As String = “MyEnterprise\”  & sGroupName
Dim AllowedMember = “[store].[stor bkey].&[" & sGroupName & "]”
Dim AMOServer As Microsoft.AnalysisServices.Server = New Microsoft.AnalysisServices.Server
Dim AMODatabase As Microsoft.AnalysisServices.Database = New Microsoft.AnalysisServices.Database
Dim AMOCube As Microsoft.AnalysisServices.Cube
Dim AMORole As Microsoft.AnalysisServices.Role
Dim AMODatabasePermission As Microsoft.AnalysisServices.DatabasePermission
Dim AMOCubePermission As Microsoft.AnalysisServices.CubePermission
Dim AMODimension As Microsoft.AnalysisServices.Dimension
Dim AMODimensionAttribute As Microsoft.AnalysisServices.DimensionAttribute
Dim AMOCubeDimensionPermission As CubeDimensionPermission
Dim AMOAttributePermission As AttributePermission
Dim AMODimensionPermission As DimensionPermission
AMOServer.Connect(MyServer
)
AMODatabase = AMOServer.Databases(MyDatabase
)
AMOCube = AMODatabase.Cubes.GetByName(MyCube
)
AMORole = AMODatabase.Roles.FindByName(sGroupName)
If Not AMORole Is Nothing Then
AMODatabase.Roles.Remove(AMODatabase.Roles.GetByName(sGroupName).ID)
End If
AMORole = AMODatabase.Roles.Add(sGroupName)
AMORole.Members.Add(
New RoleMember(RoleMember))
AMORole.Update()
AMODatabasePermission = AMODatabase.DatabasePermissions.Add(AMORole.ID)
AMODatabasePermission.Read = ReadAccess.Allowed
AMODatabasePermission.Update()
AMOCubePermission = AMOCube.CubePermissions.Add(AMORole.ID)
AMOCubePermission.Read = ReadAccess.Allowed
AMODimension = AMODatabase.Dimensions.GetByName(
“store”)
AMODimensionAttribute = AMODimension.Attributes.GetByName(
“stor bkey”)
AMOCubePermission.Update()
AMODimensionPermission = AMODimension.DimensionPermissions.Add(AMORole.ID)
AMOAttributePermission = AMODimensionPermission.AttributePermissions.Add(AMODimensionAttribute.ID)
AMOAttributePermission.AllowedSet =
“{“ & AllowedMember & “}”
AMOAttributePermission.DefaultMember = AllowedMember
AMOAttributePermission.VisualTotals = 1
AMODimensionPermission.Update()
End Sub

July 11th, 2007

Filtering Pivot Table by list of values (Excel 2007)

One of the missing features in SSAS we found is filtering by list of values. In most of clients (Excel, Panorama) you are able to set a filter for one or several items.  But filter cube by 300 items could be tricky task.   Let say we have a bussines task like: filter a Adventure Works cube by 300 different products. These 300 products spread in all Products categories hierarchy and depend to different product categories. To select 300 products you have to open Product hierarchy and check each item. To check 300 different items could be a tricky tasks, isn’t it? The solution – to find a way  programically select items.
Using Excel 2007 you can filter Pivot Table items using “VisibleItemsList” property. Look for VBA code below.

     Dim iTmp As Integer
    Dim ProductKey As String
    Dim ProductKeyArray() As String
    iTmp = 2
   
    ProductKey = Excel.Worksheets(“Filter”).Cells(iTmp, 1).Value
    While ProductKey <> Empty
        ReDim Preserve ProductKeyArray(iTmp – 2)
        ProductKeyArray(iTmp – 2) = “[Product].[Product Key].&[" & ProductKey & "]”
        iTmp = iTmp + 1
        ProductKey = Excel.Worksheets(“Filter”).Cells(iTmp, 1).Value
    Wend
   
    On Error Resume Next
    Excel.Worksheets(“Data”).PivotTables(Excel.Worksheets(“Data”).PivotTables.Count).PivotFields( _
        “[Product].[Product Key].[Product Key]“).VisibleItemsList = _
        ProductKeyArray()

 To look how this works, download file  FilterPTSample.xlsm (rename zip to FilterPTSample.xlsm). Enable macros and data connection, change data connection to Adventure Works, put product keys on sheet “Filter”, down from “A2″ and then press “Filter by product key”.  After filter was applied, in 1′st sheet (“Data”) , attribute Product Key and hierarchy “Product Categories” you have to see only products from sheet “Filter”. To clear filter, choose “Clear” from Data\Sort & Filter box.

PTBeforeFilter

ProductKeys

PTAfterFilter

May 14th, 2007

Avoid setting IsAggregatable = False to dimension attributes

In my last article I described the situation when Analysis Services reproduces incorrect results with Default Member defined on dimension attributes. Very similar problems occurs when IsAggragatable =False to dimension attributes.
The IsAggregatable property controls show or hide the topmost (aggregatable) level of hierarchy. Let say, you do not want to show the (All) level for some of you hierarchies. As example, IsAggragatable =False for scenario attribute , scenario dimension  In Adventure Works database. Therefore this attribute does not have (All) level. But be careful setting IsAggragatable =False, there may be incorrect results in some circumstances using Excel 2007. Follow the steps to reproduce behaviour and get wrong results.

Set IsAggregatable = False to [Product]/[Product categories] attribute. Connect to cube and drag [Product categories] hierarchy on filter area, Gross Profit on values. You have to see the 1′st member from [Product categories] hierarchy: Accessories. Try changing filter values from Accessories to Bikes and then to both (Accessories and Bikes). When you select both, Bikes gross profit is not taked into account! Look below for screenchots and MDX generated by Excel 2007.

Accessories

SELECT  FROM [Adventure Works] WHERE ([Product].[Product Categories].[Category].&[4],[Measures].[Gross Profit])

Bikes

SELECT  FROM [Adventure Works] WHERE ([Product].[Product Categories].[Category].&[1],[Measures].[Gross Profit])

Both with multiselect

SELECT  FROM (SELECT ({[Product].[Product Categories].[Category].&[4],[Product].[Product Categories].[Category].&[1]}) ON COLUMNS  FROM [Adventure Works]) WHERE ([Measures].[Gross Profit])

I created BUG incident in Microsoft feedback, so please everyone that is interesting in IsAggregatable, vote for this case. https://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=277058 . Hope Microsoft will then pay more attention.

May 9th, 2007

Avoid defining Default Member

Default Members are good thing in BI environment. You can define Default member as “Last month” in Date dimension, “Active” in status dimension and so on.
But be careful defining default members: Analysis Services returns incorrect results with default member in some circumstances. To reproduce buggy behaviour, open Adveture Works, define default member as [Road Bikes] subcategory in [Product].[Product Categories] hierarchy. First, set Category attribute default member to “[Product].[Category].&[1]” or [Product].[Category].[Bikes], and then set “subcategory” default member to “[Product].[Subcategory].[Subcategory].&[2]” or [Product].[Subcategory].[Subcategory].[Road Bikes].

From Excel 2007, connect to Adventure Works cube and put [Product].[Product Categories] in Filter area, Gross profit on data area. Try select [Road Bikes], [Mountain Bikes] or both ([Road Bikes], [Mountain Bikes]) in [Product].[Product Categories]. For multi-select Excel 2007 shows incorrect results, only default member is taked into account! See below screenshots and generated MDX.

Road Bikes

SELECT  FROM [Adventure Works] WHERE ([Product].[Product Categories].[Subcategory].&[1],[Measures].[Gross Profit]) CELL PROPERTIES VALUE, FORMAT_STRING, LANGUAGE, BACK_COLOR, FORE_COLOR, FONT_FLAGS

Mountain Bikes

SELECT  FROM [Adventure Works] WHERE ([Product].[Product Categories].[Subcategory].&[2],[Measures].[Gross Profit]) CELL PROPERTIES VALUE, FORMAT_STRING, LANGUAGE, BACK_COLOR, FORE_COLOR, FONT_FLAGS

Both MountainBikes and RoadBikes 

SELECT  FROM (SELECT ({[Product].[Product Categories].[Subcategory].&[1],[Product].[Product Categories].[Subcategory].&[2]}) ON COLUMNS  FROM [Adventure Works]) WHERE ([Measures].[Gross Profit]) CELL PROPERTIES VALUE, FORMAT_STRING, LANGUAGE, BACK_COLOR, FORE_COLOR, FONT_FLAGS

I created BUG incident in Microsoft feedback database and everyone could vote for it, hope Microsoft will recognize this error and pay attention on this case:
http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=275207 .

May 4th, 2007

Describing the cube

One of the features I missed in OLAP clients are objects descriptions (measures, calculated member, dimensions and so on). Only SSAS administrator could see objects descriptions. But description information for end users are also needed, even critical. I would like to see description information on OLAP objects tooltip, as example.

In our BI enviroment, we use abbraviations for some fileds. As example we have calculated member “gross margin return on investment”, but more conveniant to name this field as “GMROI” and give him description “gross margin return on investment”.   But how to set descriptions and how to show descriptions in OLAP clients? it could be a tricky task.
In Visual Studio, there is easy way to set description for a specific object. Right click on object you want to set a description and click properties. Among all other properties you’ll find “Description”.  But there is no properties for calculated members, so therefore unable to set it in Visual Mode. To workaround, open cube code (In Visual Studio, right click on cube and choose “View Code”). Locate calculated member you want to describe under <CalculationProperties> section and add additional tag <Description>. If needed, add description field for Translation as well:

        <CalculationProperty dwd:design-time-name=”96f39d00-3468-4509-9018-9bce0452ecf2″>
          <CalculationReference>[MEASURES].[GMROI]</CalculationReference>
          <CalculationType>Member</CalculationType>
          <Description>Gross Margin Return On Investment</Description>
          <Translations>
            <Translation dwd:design-time-name=”c81e34a5-77f7-4443-85a7-c33044d88c7d”>
              <Language>1063</Language>
              <Caption>GMROI</Caption>
              <Description>Investavimo grąža.</Description>
            </Translation>
          </Translations>
        </CalculationProperty>

Deploy the cube. Now, description for field GMROI is ready.
Another way to add a description on calculated member is to use third party add ins, like BIDS helper: http://www.codeplex.com/bidshelper .
But how to show the actual field name and description in front end? As I said, I do not known any clients that shows OLAP objects descriptions. So, the alternative is to write your own application. Below I pasted the sample code how to connect to analysis services, loop through cubes, then loop through every measure and calculated member in cube, write object name and they descriptions. 

    Public Sub RunAmo(ByVal DatabaseName As String)

        Dim AmoServer As New Microsoft.AnalysisServices.Server
        Dim AmoDatabase As New Microsoft.AnalysisServices.Database
        Dim AmoCube As New Microsoft.AnalysisServices.Cube
        Dim AmoDimension As New Microsoft.AnalysisServices.Dimension
        Dim AmoDimensionAttribute As New Microsoft.AnalysisServices.DimensionAttribute
        Dim AmoDimensionHierarchy As New Microsoft.AnalysisServices.Hierarchy
        Dim AmoMeasureGroup As New Microsoft.AnalysisServices.MeasureGroup
        Dim AmoMeasure As New Microsoft.AnalysisServices.Measure
        Dim AMOCalculationProperty As Microsoft.AnalysisServices.CalculationProperty
        Dim AttributesHierarchiesList(1) As String
        Dim DimensionsList(1) As String
        Dim CubesList(1) As String
        Dim MeasuresCalulationsList(1) As String ‘ Put measures and calculated members in the same array, because later this array is sorted.

        Dim iTmp As Integer = 0
        Dim jTmp As Integer = 0
        Dim kTmp As Integer = 0
        Dim count As Integer
        Dim tString As String
        Dim tStringID As String
        Dim tStringName As String
        Dim tStringDescription As String
        Dim DescriptionOK As Boolean

        AmoServer.Connect(“server_name”)

        ‘through cubes
        iTmp = 0
        For Each AmoCube In AmoServer.Databases(DatabaseName).Cubes
            count = iTmp + 1
            ReDim Preserve CubesList(count – 1)
            CubesList(iTmp) = AmoCube.Name.ToString & “|” & AmoCube.ID & “|”

            For kTmp = 0 To AmoCube.Translations.Count – 1
                If AmoCube.Translations(kTmp).Language = 1063 And Not AmoCube.Translations(kTmp).Description Is Nothing Then
                    If Not AmoCube.Translations(kTmp).Description Is Nothing Then
                        CubesList(iTmp) = CubesList(iTmp) & AmoCube.Translations(kTmp).Description
                    End If
                End If
            Next
            iTmp = count
        Next

        SortArray(CubesList)
        Response.Write(“<h3>Cubes and fields:</h3>”)
        Response.Write(“<table>”)

        For iTmp = 0 To UBound(CubesList)
            tString = CubesList(iTmp).ToString
            tStringName = Left(tString, InStr(tString, “|”, CompareMethod.Text) – 1)
            tStringID = Mid(tString, InStr(tString, “|”, CompareMethod.Text) + 1, InStr(InStr(tString, “|”, CompareMethod.Text) + 1, tString, “|”) – InStr(tString, “|”, CompareMethod.Text) – 1)
            tStringDescription = Mid(tString, InStr(InStr(tString, “|”, CompareMethod.Text) + 1, tString, “|”) + 1)

            Response.Write(“<tr><tdclsCubeHeader”">Fields for cube <spanclsCubeName”">” & tStringName & “</span></td><tdclsCubeHeader”">” & tStringDescription & “</td></tr>”)
            AmoCube = AmoServer.Databases(DatabaseName).Cubes(tStringID)

            jTmp = 0
            For Each AmoMeasureGroup In AmoCube.MeasureGroups
                For Each AmoMeasure In AmoMeasureGroup.Measures
                    If AmoMeasure.Visible = True Then
                        count = jTmp + 1
                        ReDim Preserve MeasuresCalulationsList(count – 1)

                        For kTmp = 0 To AmoMeasure.Translations.Count – 1
                            If AmoMeasure.Translations(kTmp).Language = 1063 Then
                                If Not AmoMeasure.Translations(kTmp).Caption Is Nothing Then
                                    MeasuresCalulationsList(jTmp) = AmoMeasure.Translations(kTmp).Caption.ToString & “|”
                                Else
                                    MeasuresCalulationsList(jTmp) = AmoMeasure.Name & “|”
                                End If
                                Exit For
                            End If
                        Next
                        DescriptionOK = False
                        For kTmp = 0 To AmoMeasure.Translations.Count – 1
                            If AmoMeasure.Translations(kTmp).Language = 1063 Then
                                If Not AmoMeasure.Translations(kTmp).Description Is Nothing Then
                                    MeasuresCalulationsList(jTmp) = MeasuresCalulationsList(jTmp) & AmoMeasure.Translations(kTmp).Description.ToString & “<br>”
                                    DescriptionOK = True
                                End If
                                Exit For
                            End If
                        Next
                        If Not AmoMeasure.Description Is Nothing And DescriptionOK = False Then
                            MeasuresCalulationsList(jTmp) = MeasuresCalulationsList(jTmp) & AmoMeasure.Description.ToString & “<br>”
                        End If

                        jTmp = count
                    End If
                Next    ‘AmoMeasure In AmoMeasureGroup.Measures
            Next    ‘AmoMeasureGroup In AmoCube.MeasureGroups
            If AmoCube.MdxScripts.Count > 0 Then
                For Each AMOCalculationProperty In AmoCube.MdxScripts(0).CalculationProperties
                    ‘If AMOCalculationProperty.Visible = True Then
                    count = jTmp + 1
                    ReDim Preserve MeasuresCalulationsList(count – 1)

                    If AMOCalculationProperty.Translations.Count <> 0 Then
                        MeasuresCalulationsList(jTmp) = AMOCalculationProperty.Translations(0).Caption.ToString & “|”
                    Else
                        MeasuresCalulationsList(jTmp) = AMOCalculationProperty.Description.ToString & “|”
                    End If

                    DescriptionOK = False
                    If AmoMeasure.Translations.Count <> 0 Then
                        If Not AMOCalculationProperty.Translations(0).Description Is Nothing Then
                            MeasuresCalulationsList(jTmp) = MeasuresCalulationsList(jTmp) & AMOCalculationProperty.Translations(0).Description.ToString & “<br>”
                            DescriptionOK = True
                        End If
                    End If
                    If Not AMOCalculationProperty.Description Is Nothing And DescriptionOK = False Then
                        MeasuresCalulationsList(jTmp) = MeasuresCalulationsList(jTmp) & AMOCalculationProperty.Description.ToString & “<br>”
                    End If

                    jTmp = count

                Next    ‘AMOCalculationProperty In AmoCube.MdxScripts(0).CalculationProperties
            End If

            SortArray(MeasuresCalulationsList) ‘Call external function to sort MeasuresCalulationsList
            For jTmp = 0 To UBound(MeasuresCalulationsList)
                tString = MeasuresCalulationsList(jTmp).ToString
                tStringName = Left(tString, InStr(tString, “|”, CompareMethod.Text) – 1)
                tStringDescription = Mid(tString, InStr(tString, “|”, CompareMethod.Text) + 1)

                Response.Write(“<tr><tdclsMeasures”">”)
                Response.Write(tStringName)
                Response.Write(“</td><td clsMeasuresDescription”">”)
                Response.Write(tStringDescription)
                Response.Write(“<tr><td>”)

            Next
        Next
        Response.Write(“</table”)

    End Sub

This is only way to describe you cube.

May 2nd, 2007

Choosing client for SSAS 2005: Excel 2003 or Excel 2007

After we migrated Analysis Services from 2000 to 2005 we discovered that the same (or analogous) queries against SSAS 2005 runs even slower than AS 2000. Our users used MS Excel 2003 as front end on AS 2005. After some time I installed MS Excel 2007. Then I tried to do the same Pivot Table actions on both Excel versions (2003 and 2007): make Pivot Table from Advenure Works database placing days of [Date].[Calendar] [July 2003] and [August 2003] on rows, [Mountain-200 Silver, 38] and [Mountain-200 Silver, 42] from product [Product].[categories] on columns and [Sales Amount] as facts
 

Excel 2003 genetares cumbersome MDX wits lots of except:

SELECT NON EMPTY HIERARCHIZE(Except({AddCalculatedMembers(Except({AddCalculatedMembers(Except({AddCalculatedMembers(DrillDownMember({Except({AddCalculatedMembers(DrillDownMember({DrillDownLevel({[Product].[Prod
uct Categories].[All Products]})}, {[Product].[Product Categories].[Category].&[1]}))}, {[Product].[Product Categories].[Category].&[2], [Product].[Product Categories].[Category].&[3], [Product].[Product
Categories].[Category].&[4]})}, {[Product].[Product Categories].[Subcategory].&[1]}))}, {[Product].[Product Categories].[Subcategory].&[2], [Product].[Product Categories].[Subcategory].&[3]}))},
{[Product].[Product Categories].[Product].&[600], [Product].[Product Categories].[Product].&[599], [Product].[Product Categories].[Product].&[598], [Product].[Product Categories].[Product].&[597],
 [Product].[Product
Categories].[Product].&[596], [Product].[Product Categories].[Product].&[595], [Product].[Product Categories].[Product].&[594], [Product].[Product Categories].[Product].&[593], [Product].[Product
Categories].[Product].&[592], [Product].[Product Categories].[Product].&[591], [Product].[Product Categories].[Product].&[590], [Product].[Product Categories].[Product].&[589], [Product].[Product
Categories].[Product].&[588], [Product].[Product Categories].[Product].&[587], [Product].[Product Categories].[Product].&[367], [Product].[Product Categories].[Product].&[366], [Product].[Product
Categories].[Product].&[365], [Product].[Product Categories].[Product].&[364], [Product].[Product Categories].[Product].&[363], [Product].[Product Categories].[Product].&[362], [Product].[Product
Categories].[Product].&[361], [Product].[Product Categories].[Product].&[360], [Product].[Product Categories].[Product].&[359], [Product].[Product Categories].[Product].&[358], [Product].[Product
Categories].[Product].&[357], [Product].[Product Categories].[Product].&[356], [Product].[Product Categories].[Product].&[352], [Product].[Product Categories].[Product].&[351], [Product].[Product
Categories].[Product].&[350], [Product].[Product Categories].[Product].&[349], [Product].[Product Categories].[Product].&[348], [Product].[Product Categories].[Product].&[347], [Product].[Product
Categories].[Product].&[346], [Product].[Product Categories].[Product].&[345], [Product].[Product Categories].[Product].&[344], [Product].[Product Categories].[Product].&[311], [Product].[Product
Categories].[Product].&[310], [Product].[Product Categories].[Product].&[606], [Product].[Product Categories].[Product].&[605], [Product].[Product Categories].[Product].&[604], [Product].[Product
Categories].[Product].&[584], [Product].[Product Categories].[Product].&[583], [Product].[Product Categories].[Product].&[582], [Product].[Product Categories].[Product].&[581], [Product].[Product
Categories].[Product].&[580], [Product].[Product Categories].[Product].&[390], [Product].[Product Categories].[Product].&[389], [Product].[Product Categories].[Product].&[388], [Product].[Product
Categories].[Product].&[387], [Product].[Product Categories].[Product].&[386], [Product].[Product Categories].[Product].&[385], [Product].[Product Categories].[Product].&[384], [Product].[Product
Categories].[Product].&[383], [Product].[Product Categories].[Product].&[382], [Product].[Product Categories].[Product].&[381], [Product].[Product Categories].[Product].&[380], [Product].[Product
Categories].[Product].&[379], [Product].[Product Categories].[Product].&[378], [Product].[Product Categories].[Product].&[377], [Product].[Product Categories].[Product].&[376], [Product].[Product
Categories].[Product].&[375], [Product].[Product Categories].[Product].&[374], [Product].[Product Categories].[Product].&[373], [Product].[Product Categories].[Product].&[372], [Product].[Product
Categories].[Product].&[371], [Product].[Product Categories].[Product].&[370], [Product].[Product Categories].[Product].&[369], [Product].[Product Categories].[Product].&[368], [Product].[Product
Categories].[Product].&[343], [Product].[Product Categories].[Product].&[342], [Product].[Product Categories].[Product].&[341], [Product].[Product Categories].[Product].&[340], [Product].[Product
Categories].[Product].&[339], [Product].[Product Categories].[Product].&[338], [Product].[Product Categories].[Product].&[337], [Product].[Product Categories].[Product].&[336], [Product].[Product
Categories].[Product].&[335], [Product].[Product Categories].[Product].&[334], [Product].[Product Categories].[Product].&[333], [Product].[Product Categories].[Product].&[332], [Product].[Product
Categories].[Product].&[331], [Product].[Product Categories].[Product].&[330], [Product].[Product Categories].[Product].&[329], [Product].[Product Categories].[Product].&[328], [Product].[Product
Categories].[Product].&[327], [Product].[Product Categories].[Product].&[326], [Product].[Product Categories].[Product].&[325], [Product].[Product Categories].[Product].&[324], [Product].[Product
Categories].[Product].&[323], [Product].[Product Categories].[Product].&[322], [Product].[Product Categories].[Product].&[321], [Product].[Product Categories].[Product].&[320], [Product].[Product
Categories].[Product].&[319], [Product].[Product Categories].[Product].&[318], [Product].[Product Categories].[Product].&[317]}))}, {[Product].[Product Categories].[Category].&[2], [Product].[Product
Categories].[Category].&[3], [Product].[Product Categories].[Category].&[4], [Product].[Product Categories].[Subcategory].&[2], [Product].[Product Categories].[Subcategory].&[3], [Product].[Product
Categories].[Product].&[600], [Product].[Product Categories].[Product].&[599], [Product].[Product Categories].[Product].&[598], [Product].[Product Categories].[Product].&[597], [Product].[Product
Categories].[Product].&[596], [Product].[Product Categories].[Product].&[595], [Product].[Product Categories].[Product].&[594], [Product].[Product Categories].[Product].&[593], [Product].[Product
Categories].[Product].&[592], [Product].[Product Categories].[Product].&[591], [Product].[Product Categories].[Product].&[590], [Product].[Product Categories].[Product].&[589], [Product].[Product
Categories].[Product].&[588], [Product].[Product Categories].[Product].&[587], [Product].[Product Categories].[Product].&[367], [Product].[Product Categories].[Product].&[366], [Product].[Product
Categories].[Product].&[365], [Product].[Product Categories].[Product].&[364], [Product].[Product Categories].[Product].&[363], [Product].[Product Categories].[Product].&[362], [Product].[Product
Categories].[Product].&[361], [Product].[Product Categories].[Product].&[360], [Product].[Product Categories].[Product].&[359], [Product].[Product Categories].[Product].&[358], [Product].[Product
Categories].[Product].&[357], [Product].[Product Categories].[Product].&[356], [Product].[Product Categories].[Product].&[352], [Product].[Product Categories].[Product].&[351], [Product].[Product
Categories].[Product].&[350], [Product].[Product Categories].[Product].&[349], [Product].[Product Categories].[Product].&[348], [Product].[Product Categories].[Product].&[347], [Product].[Product
Categories].[Product].&[346], [Product].[Product Categories].[Product].&[345], [Product].[Product Categories].[Product].&[344], [Product].[Product Categories].[Product].&[311], [Product].[Product
Categories].[Product].&[310], [Product].[Product Categories].[Product].&[606], [Product].[Product Categories].[Product].&[605], [Product].[Product Categories].[Product].&[604], [Product].[Product
Categories].[Product].&[584], [Product].[Product Categories].[Product].&[583], [Product].[Product Categories].[Product].&[582], [Product].[Product Categories].[Product].&[581], [Product].[Product
Categories].[Product].&[580], [Product].[Product Categories].[Product].&[390], [Product].[Product Categories].[Product].&[389], [Product].[Product Categories].[Product].&[388], [Product].[Product
Categories].[Product].&[387], [Product].[Product Categories].[Product].&[386], [Product].[Product Categories].[Product].&[385], [Product].[Product Categories].[Product].&[384], [Product].[Product
Categories].[Product].&[383], [Product].[Product Categories].[Product].&[382], [Product].[Product Categories].[Product].&[381], [Product].[Product Categories].[Product].&[380], [Product].[Product
Categories].[Product].&[379], [Product].[Product Categories].[Product].&[378], [Product].[Product Categories].[Product].&[377], [Product].[Product Categories].[Product].&[376], [Product].[Product
Categories].[Product].&[375], [Product].[Product Categories].[Product].&[374], [Product].[Product Categories].[Product].&[373], [Product].[Product Categories].[Product].&[372], [Product].[Product
Categories].[Product].&[371], [Product].[Product Categories].[Product].&[370], [Product].[Product Categories].[Product].&[369], [Product].[Product Categories].[Product].&[368], [Product].[Product
Categories].[Product].&[343], [Product].[Product Categories].[Product].&[342], [Product].[Product Categories].[Product].&[341], [Product].[Product Categories].[Product].&[340], [Product].[Product
Categories].[Product].&[339], [Product].[Product Categories].[Product].&[338], [Product].[Product Categories].[Product].&[337], [Product].[Product Categories].[Product].&[336], [Product].[Product
Categories].[Product].&[335], [Product].[Product Categories].[Product].&[334], [Product].[Product Categories].[Product].&[333], [Product].[Product Categories].[Product].&[332], [Product].[Product
Categories].[Product].&[331], [Product].[Product Categories].[Product].&[330], [Product].[Product Categories].[Product].&[329], [Product].[Product Categories].[Product].&[328], [Product].[Product
Categories].[Product].&[327], [Product].[Product Categories].[Product].&[326], [Product].[Product Categories].[Product].&[325], [Product].[Product Categories].[Product].&[324], [Product].[Product
Categories].[Product].&[323], [Product].[Product Categories].[Product].&[322], [Product].[Product Categories].[Product].&[321], [Product].[Product Categories].[Product].&[320], [Product].[Product
Categories].[Product].&[319], [Product].[Product Categories].[Product].&[318], [Product].[Product Categories].[Product].&[317]})) DIMENSION PROPERTIES PARENT_UNIQUE_NAME ON COLUMNS , NON EMPTY
HIERARCHIZE(Except({AddCalculatedMembers(Except({AddCalculatedMembers(DrillDownMember({Except({AddCalculatedMembers(DrillDownMember({Except({AddCalculatedMembers(DrillDownMember({Except({Add
CalculatedMembers(DrillDownMember({DrillDownLevel({[Date].[Calendar].[All Periods]})}, {[Date].[Calendar].[Calendar Year].&[2003]}))}, {[Date].[Calendar].[Calendar Year].&[2004], [Date].[Calendar].[Calendar
Year].&[2002], [Date].[Calendar].[Calendar Year].&[2001]})}, {[Date].[Calendar].[Calendar Semester].&[2003]&[2]}))}, {[Date].[Calendar].[Calendar Semester].&[2003]&[1]})}, {[Date].[Calendar].[Calendar
Quarter].&[2003]&[3]}))}, {[Date].[Calendar].[Calendar Quarter].&[2003]&[4]})}, {[Date].[Calendar].[Month].&[2003]&[7], [Date].[Calendar].[Month].&[2003]&[8]}))}, {[Date].[Calendar].[Month].&[2003]&[9]}))},
{[Date].[Calendar].[Calendar Year].&[2004], [Date].[Calendar].[Calendar Year].&[2002], [Date].[Calendar].[Calendar Year].&[2001], [Date].[Calendar].[Calendar Semester].&[2003]&[1], [Date].[Calendar].[Calendar
Quarter].&[2003]&[4], [Date].[Calendar].[Month].&[2003]&[9]})) DIMENSION PROPERTIES PARENT_UNIQUE_NAME ON ROWS FROM [Adventure Works] WHERE ([Measures].[Sales Amount])

Excel 2007 generates elegant MDX:

SELECT NON EMPTY Hierarchize({DrilldownLevel({[Product].[Product Categories].[All Products]})}) DIMENSION PROPERTIES PARENT_UNIQUE_NAME ON COLUMNS , NON EMPTY
Hierarchize(DrilldownMember({{DrilldownMember({{DrilldownMember({{DrilldownMember({{DrilldownLevel({[Date].[Calendar].[All Periods]})}}, {[Date].[Calendar].[Calendar Year].&[2003]})}},
{[Date].[Calendar].[Calendar Semester].&[2003]&[2]})}}, {[Date].[Calendar].[Calendar Quarter].&[2003]&[3]})}}, {[Date].[Calendar].[Month].&[2003]&[7],[Date].[Calendar].[Month].&[2003]&[8]})) DIMENSION
PROPERTIES PARENT_UNIQUE_NAME,[Date].[Calendar].[Calendar Semester].[Calendar Year],[Date].[Calendar].[Calendar Quarter].[Calendar Quarter of Year],[Date].[Calendar].[Calendar Quarter].[Calendar
Semester],[Date].[Calendar].[Calendar Quarter].[Calendar Semester of Year],[Date].[Calendar].[Calendar Quarter].[Fiscal Quarter],[Date].[Calendar].[Month].[Calendar Quarter],[Date].[Calendar].[Month].[Month of
Year],[Date].[Calendar].[Date].[Day Name],[Date].[Calendar].[Date].[Day of Year],[Date].[Calendar].[Date].[Day of Month],[Date].[Calendar].[Date].[Day of Week],[Date].[Calendar].[Date].[Month
Name],[Date].[Calendar].[Date].[Week of Year] ON ROWS  FROM (SELECT ({[Product].[Product Categories].[Product].&[352], [Product].[Product Categories].[Product].&[353], [Product].[Product
Categories].[Product].&[354], [Product].[Product Categories].[Product].&[355]}) ON COLUMNS , ({[Date].[Calendar].[Month].&[2003]&[8], [Date].[Calendar].[Month].&[2003]&[7]}) ON ROWS  FROM [Adventure Works])
WHERE ([Measures].[Sales Amount]) CELL PROPERTIES VALUE, FORMAT_STRING, LANGUAGE, BACK_COLOR, FORE_COLOR, FONT_FLAGS

The 1′st query from Excel 2003 runs 5 times longer! Profiler shows lots of “Started/finised reading data from the … partitions

 excel_2003_profiler.jpg

So, we started to move client to new Excel 2007 version. After some time I gathered statistics from profiler and querylog. The results are as follows: 

Avg MDX text length (Excel 2003): 2403
Avg MDX text length (Excel 2007): 1567
Avg query duration (Excel 2003): 507 ms
Avg query duration (Excel 2007): 96 ms

From my expirience, almost impossible to work with SSAS 2005 with Excel 2003. Avoid using this as a client and move to new Excel 2007. 

Excel 2007 also has backward compatibility mode. It means Excel 2007  generates queries for AS 2000, as example with lots of except. To take advantages working with Excel 2007 ensure you have converted Excel 2007 from “compatibility mode” and recreated pivot tables. Among all other Excel 2007 features you’ll get the best performance.

|