SSAS: Reporting on Metadata


I had a comment recently on one of my older posts that I did about XMLA which asked about listing cubes in a database and whether the cube is processed and it's size.

I have posted about a number of different ways of doing this over the last few years, but I thought I would use this opportunity to consolidate a list all the various ways of doing this that I could think of. Under the covers all these techniques are ultimately issuing some sort of XMLA discover against the SSAS database and returning a result, it's just that they all use different levels and types abstractions.

  • Using XMLA Discover 
    The XMLA discover command returns the results we want, but being an XML result it is not the most user friendly thing to read.
  • Using VBScript
    Vidas has an example which produces nice output in it's own right, but not easy to incorporate into a report.
  • Using Powershell
    Using powerSSAS you can interactively navigate through your SSAS database and inspect objects and properties or you could write a script, but as with the VBScript example, you cannot really use a Powershell script as a data source for a report.
  • Using ASSP
    I built the Discover and DMV functions for the Analysis Services Stored Procedure project that allow you to execute discover commands and return the results as a data table
  • Using ASSP Reports
    This is really a subset of the point above, but using these stored procedures I built a sample report in Reporting Services that displays all the cubes and partitions and their processed status for a given database. I would loved to have incorporated this report into SSMS, but SSMS does not allow custom reports for Analysis Services and it does not allow add-ins like we have in BIDS (ie. BIDS Helper) so there is no supported way to add this functionality at the moment.
  • SSAS 2008 - DMVs
    In the 2008 version of SSAS it will have native DMV functionality which produces similar to the ASSP stored procedures.

 

author: Darren Gosbell | posted @ Tuesday, May 06, 2008 10:08 AM | Feedback (0)

powerSSAS: Listing Role information


After my last post on how to list the aggregations for a given attribute using Powershell and PowerSSAS, miltruenos commented, asked if it was possible to do a similar script to show roles and allowed/denied sets, so here is just such a script.

add-PSSnapin powerSSAS
new-PSDrive
ssas powerssas localhost

$roles = gi "\Databases\Adventure Works DW\"  `

   | % {$_.Dimensions} `
   | % {$_
.DimensionPermissions} `
   | % {$_
.AttributePermissions} `
   | add-Member ScriptProperty DimensionName {$this.Parent.Parent.Name}-passthru
`
   | add-Member ScriptProperty RoleName {$this.Parent.Role}-passthru


$roles |sort-Object RoleName '
  
|Format-list DimensionName, Attribute, AllowedSet, DeniedSet -groupby
RoleName

However I realise that not everyone may be able to load PowerSSAS on their machines. So I thought I might also show how you could do the same thing with "pure" PowerShell. You can see that the core of the script is essentially the same, powerSSAS is really just a thin wrapper over AMO. The main difference is at the top where I have to load the AMO library and connect to the server and get a reference to the database.

$svrName = "localhost"
$DBname = "Adventure Works DW"
### load the AMO library (redirect to null to 'eat' the output from assembly loading process)
[System.Reflection.Assembly]::LoadwithpartialName("Microsoft.AnalysisServices") > $null
# connect to the AS Server
$svr = New-Object Microsoft.AnalysisServices.Server
$svr.Connect($svrName)
# get a reference to the database
$db= $svr.Databases.Item($DBname)

$roles = $db | % {$_.Dimensions} `
   | % {$_.DimensionPermissions} `
   | % {$_.AttributePermissions} `
   | add-Member ScriptProperty DimensionName {$this.Parent.Parent.Name} -passthru `
   | add-Member ScriptProperty RoleName {$this.Parent.Role} -passthru
$roles |sort-Object RoleName '
  
|Format-list DimensionName, Attribute, AllowedSet, DeniedSet -groupby
RoleName
$svr.disconnect()
 

I tried to find a way to make the format-list command put a break between each object, but I could not see a way of doing it. If you prefer you could replace the format-list with format-table and add the -wrap parameter, but I think that makes the allowed and denied sets a bit harder to read.

Note: Last year I added a report to the Analysis Services Stored Procedure project which also shows this sort of information. I think it still might be a bit rough, but it does give some idea of what is possible.

Technorati Tags: ,

author: Darren Gosbell | posted @ Thursday, May 01, 2008 10:29 PM | Feedback (2)

powerSSAS: Getting Aggregations for a particular Attribute


If you have ever manually edited aggregation designs, you may have wondered what aggregation currently exist for a particular attribute. Well the following powerSSAS script will give you just that. (you could do this with straight PowerShell, but powerSSAS just makes it slightly easier to address a given measure group)

Technically this script is just one single statement, with a couple of parameters to make it more re-usable. I have broken it up in order to make more readable by using the PowerShell line continuation character which is the back tick (`). I also pretty much developed it, starting off at the top and slowly drilling down adding one "line" at a time.

Before I (try to) explain what is going on here I thought I should explain some of the PowerShell notations that this script uses.

| pipes the objects output from the previous command into the next command
% shorthand for "for each"
{ } this is a script block, when it comes after % the script block is executed once for each object in the collection that was passed in.
; command separator
$_ denotes the current object
-eq does an equality comparison, in powershell the = sign is only used for assigning values

So here is the script:

1
2

3
4

5
6


7
8
9
10
11
12
13
14
15
16
17
18
19
20

add-PSSnapin powerssas
new-PSDrive ssas powerssas localhost

function getAggsForAttrib ([String]$MeasureGroupPath, [String]$Attribute)
{

(get-Item $MeasureGroupPath).AggregationDesigns `
    | % {  $_.Aggregations } `
    | % {  $agg = $_ ; $agg} `
    | % { $_.Dimensions} `
    | % {$_.Attributes} `
    | % {$_.Attribute} `
    | Where-Object {$_.Name -eq $Attribute} `
    | %{$agg} `
    | % { $_.Dimensions} `
    | % {  $curDim = $_ ; $curDim} `
    | % {$_.Attributes} `
    | % {$_.Attribute} `
    | add-Member ScriptProperty AggregationName {$agg.Name } -passthru `
    | add-Member scriptProperty CubeDimension {$curDim.CubeDimension} -passthru `
    | Format-Table AggregationName, CubeDimension, name
}  

Now, I'm sort of conscious that most of the people that are reading this will more more into Analysis Services than PowerShell. So for those of you that are interested, below is a rough outline of what is going on in this function. If you are just interested in the output, you can skip ahead to see what the output looks like.

lines 1 & 2 - these load powerSSAS and setup a connection to the localhost, if you are working with a remote instance you would just need to replace localhost with the name of the instance.

lines 3,4 & 20 - this is the function declaration

line 5 - This is where we get the Aggregation design for the specified Measure Group

line 6 - this can be translated into English as "for each AggregationDesign object that is piped in, echo out the Aggregations collection"

line 7 - for each aggregation in the Aggregations collection that was passed in, store the value in a variable called $agg (created on the fly) and echo out the contents of that variable.

lines 8-10 - for each Dimension in the Aggregation that was passed in get the Attributes collection and the individual Attributes

line 11 - filter all the Attribute objects so that we only return those where the name is the same as the $Attribute parameter

line 12 - echo out the $agg variable that was associated with the attributes that were filtered out in line 11.

lines 13-14 - grab the dimensions from the filtered aggregations and stored them in the $curDim variable and echo the contents of that variable to the next step in the pipeline.

lines 15-16 - output the set of other Attributes that are part of the aggregation design

lines 17-18 - to the Attributes which we are outputting from the previous step add a couple of dynamic member properties, one for the name of the aggregation and another for the name of the dimension.

line 19 - format the output as a table, only displaying the selected properties.    

And here is what happens when you ask the function for aggregations for the Internet Sales measure group in the Adventure Works DW database that include the Sales Territory Country attribute.


PS> getAggsForAttrib "ssas:\databases\Adventure works dw\cubes\adventure works\measuregroups\internet sales" "Sales Territory Country"

AggregationName          CubeDimension          Name                                  
---------------          -------------          ----                                  
Aggregation 8            Promotion              Promotion Category                    
Aggregation 8            Sales Territory        Sales Territory Country               
Aggregation d            Sales Territory        Sales Territory Country               
Aggregation d            Product                Days to Manufacture                   
Aggregation e            Sales Territory        Sales Territory Country               
Aggregation e            Product                Product Line                          
Aggregation f            Sales Territory        Sales Territory Country               
Aggregation f            Date                   Fiscal Year                           
Aggregation 10           Sales Territory        Sales Territory Country               
Aggregation 10           Date                   Calendar Year                         
Aggregation 11           Sales Territory        Sales Territory Country               
Aggregation 11           Ship Date              Fiscal Year                           
Aggregation 12           Sales Territory        Sales Territory Country               
Aggregation 12           Ship Date              Calendar Year                         
Aggregation 13           Sales Territory        Sales Territory Country               
Aggregation 13           Delivery Date          Fiscal Year                           
Aggregation 14           Sales Territory        Sales Territory Country               
Aggregation 14           Delivery Date          Calendar Year                         
Aggregation 15           Sales Territory        Sales Territory Country               
Aggregation 15           Product                Category  
           

And here is what happens when you ask the function for aggregations for the Internet Sales measure group in the Adventure Works DW database that include the Calendar Semester attribute. Note that the Date dimension is used a couple of times in the Adventure Works cube as a role playing dimension (Date, Ship Date, Delivery Date)

PS> getAggsForAttrib "ssas:\databases\Adventure works dw\cubes\adventure works\measuregroups\internet sales" "Calendar Semester"

AggregationName          CubeDimension          Name                                  
---------------          -------------          ----                                  
Aggregation 2            Promotion              Promotion Category                    
Aggregation 2            Delivery Date          Calendar Semester                     
Aggregation 4            Promotion              Promotion Category                    
Aggregation 4            Ship Date              Calendar Semester                     
Aggregation 7            Promotion              Promotion Category                    
Aggregation 7            Date                   Calendar Semester
 


Technorati Tags: ,

author: Darren Gosbell | posted @ Sunday, April 27, 2008 10:46 PM | Feedback (1)

More BI MVP goodness


I just learned that we have some new BI MVPs that were awarded recently

On the SSAS front Vidas Matelis (of www.ssas-info.com fame) has received a SQL Server MVP award.

And two of my fellow countrymen and PerformancePoint specialists Adrian Downes and Nick Barclay have both been awarded too.

author: Darren Gosbell | posted @ Wednesday, April 02, 2008 9:36 AM | Feedback (0)

Using PowerShell to update the case of Attribute names


I had a question recently about how to quickly convert all the attributes in a given database from UPPER CASE to Title Case. Now you could certainly write a C# or VB.Net based program, but I feel that a script is the way to go for a once off requirement like this and what better scripting environment could we ask for than PowerShell. So here is a short PowerShell script that does the job.

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.AnalysisServices") > $null
[Microsoft.AnalysisServices.Server]$svr = new-Object([Microsoft.AnalysisServices.Server])
$svr.Connect("Localhost")
$DatabaseID = "Adventure Works DW"
$db = $svr.Databases.Item($DatabaseID)

# This small function is primarily extracted to improve readability
function ToProperCase([String]$in)
{
[System.Threading.Thread]::CurrentThread.CurrentCulture.TextInfo.ToTitleCase($in.ToLower())
( Get-Culture ).TextInfo.ToTitleCase($in.ToLower())
} # Foreach dimension, loop through each attribute and call the ToProperCase function $db.Dimensions | % {$_.Attributes | % {$_.Name = ToProperCase $_.Name}; $_.Update()}
# Print out all the Attributes for each dimension
$db.Dimensions | % {"Dimension: " + $_.Name; $_.Attributes | % {"   " + $_.Name }}

$svr.Disconnect()

The key to the whole routine is the following line, which probably looks a bit cryptic at first.

$db.Dimensions| % {$_.Attributes| % {$_.Name = ToProperCase $_.Name}; $_.Update()}

There are 4 PowerShell features in operation here
|  - the pipe character, which passes the output of the previous command as input to the next one
%  - the foreach operator which iterates over all the objects in a collection
$_ - the "current object" variable which returns the current object during each iteration of the foreach loop
;  - is the statement terminator

So in English the line of PowerShell above reads as follows: "Get the collection of dimensions and pass it to a for-each loop. For each dimension get the collection of Attributes and pass it to a for-each loop. For each Attribute, set the Name property equal to the Proper Case version of the Name. After processing all the attributes call the Update() method of the dimension."

In C# the single line of PowerShell would look like the follow:

foreach (Microsoft.AnalysisServices.Dimension dim in db.Dimensions)
{
  foreach (Microsoft.AnalysisServices.Attribute att in dbdim.Attributes)
    {
      att.Name = ToProperCase(att.Name);
    }
    dim.Update();
}

It's not hard to modify the main line to alter the name of all the dimensions in the database by using something like the following:

$db.Dimensions | % {$_.Name = ToProperCase $_.Name; $_.Update()}

Changing all the measure names can be done with the same technique, but I leave that as a exercise for the reader.

Update 17 Mar 08: Thanks to /\/\o\/\/ for a much simpler way to get the current culture.
Update 26 Mar 08: Corrected a slight typo in the C# translation (thanks Richard)

Technorati Tags: ,

author: Darren Gosbell | posted @ Monday, March 17, 2008 12:01 AM | Feedback (3)

Analysis Services Team Update posting on the MSDN forum


I just spotted an interesting post on the Analysis Services forum. It was titled "Analysis Services Team Update" and was posted by Ariel Netz, Group Program Manager for Analysis Services.

He starts off with ..

"I could never understand people’s fascination with blogs."

...which just begged to be blogged about. :)

In it he talks about how Analysis Services is looking for the SQL Server 2008 "Katmai" release

"In all honesty, things are looking good. In fact, looking very good (relative to where we are in the release cycle), so much so that it makes me somewhat nervous…  "

And he goes on to talk about how CTP6, which is also know as the February CTP is "feature complete" and that they are pretty much in a stabilizing phase now leading up to the release.

He also goes on to talk about how important it is to log any bugs on the connect site http://connect.microsoft.com/sql as bugs reported here are given much more weight by the team than one which was only found by the internal test teams.

It's an interesting read - you can read the full post here: http://forums.microsoft.com/msdn/ShowPost.aspx?siteid=1&PostID=2987119

Technorati Tags:

author: Darren Gosbell | posted @ Wednesday, March 12, 2008 9:03 PM | Feedback (0)

SSAS 2008 - small DMV improvement


I was just checking the DMVs in SSAS 2008 CTP6 to see if they had changed between CPT5 & 6. Particularly in regard to the list of limitations that Vidas posted. As far as I can tell the only thing that appears to have changed is that "SELECT DISTINCT" now appears to work.

Technorati Tags: ,

author: Darren Gosbell | posted @ Friday, February 22, 2008 9:21 AM | Feedback (0)

SSAS 2008: Displaying Hierarchical data from DMVs


I just noticed an interesting thing about the schema rowsets when queried using the new DMV syntax and that is that you can drill into hierarchical data - cool.

image4_thumb1

This probably comes out of building the DMV syntax on top of DMX which seems to make a bit of use of nested tables.

author: Darren Gosbell | posted @ Friday, February 22, 2008 7:29 AM | Feedback (0)

SSAS 2008: What's new in the Schema Rowsets?


I was asking myself this question recently and while I could not find anything in Books Online, it is not too hard to figure this out for ourselves. The following comparison was run between SSAS 2005 SP2 and the February CTP (CTP6) of SSAS 2008.

I really love the way that you can discover the metadata in SSAS, all I had to do is to run a DISCOVER_SCHEMA_ROWSETS XML/A command against each server and then use XMLNotepad 2007 to compare the resulting XML.

In the SQL Server 2008 you should find that features are not included in a CTP until they are pretty much complete, including documentation. So you can find documentation for all of this stuff under the "Schema Rowsets" section in Books Online (BOL) here: http://msdn2.microsoft.com/en-us/library/ms126233(SQL.100).aspx. In fact I have used a number of snippets from BOL in the rest of this post.

Changes to existing Rowsets

MDSCHEMA_MEMBERS Added SCOPE (int) restriction

from BOL:

The scope of the member. The member can be a session calculated member or global calculated member. The column returns NULL for non-calculated members.

This column can have one of the following values:

MDMEMBER_SCOPE_GLOBAL=1

MDMEMBER_SCOPE_SESSION=2

MDSCHEMA_SETS Added SET_EVALUATION_CONTEXT (int) restriction

from BOL:

The context for the set. The set can be static or dynamic.

This column can have one of the following values:

MDSET_RESOLUTION_STATIC=1

MDSET_RESOLUTION_DYNAMIC=2

MDSCHEMA_KPIS Added SCOPE (int) restriction
  (see above for a definition of SCOPE)
DISCOVER_TRACE_EVENT_CATEGORIES changed SPID restriction from string to unsignedInt
changed MemoryUsed restriction from int to long
changed BaseObjectType restriction from string to unsignedInt
DISCOVER_SESSIONS

Added SESSION_STATUS (int) restriction

from BOL:

The activity status of the session.

0 means "Idle": No current activity is ongoing.

1 means "Active": The session is executing some requested task.

2 means is "Blocked": The session is waiting for some resource to continue executing the suspended task.

 

New Rowsets for SQL 2008 (as of CTP6)

I found 4 completely new rowsets DISCOVER_COMMANDS, DISCOVER_COMMAND_OBJECTS, DISCOVER_OBJECT_ACTIVITY and DISCOVER_OBJECT_MEMORY_USAGE. I have included a brief overview and a link to the MSDN page for each of them below.

 

Name DISCOVER_COMMANDS
Restrictions SESSION_SPID (int)
Columns SESSION_SPID (int)
SESSION_COMMAND_COUNT (int)COMMAND_START_TIME (dateTime)
COMMAND_ELAPSED_TIME_MS (long)
COMMAND_CPU_TIME_MS (long)
COMMAND_READS (long)
COMMAND_READ_KB (long)
COMMAND_WRITES (long)
COMMAND_WRITE_KB (long)

Provides resource usage and activity information about the currently executing or last executed commands in the opened connections on the server.

for more details see: http://msdn2.microsoft.com/en-us/library/bb934103(SQL.100).aspx

image

 

Name DISCOVER_COMMAND_OBJECTS
Restrictions SESSION_SPID (int)
SESSION_ID (string)
OBJECT_PARENT_PATH (string)
OBJECT_ID (string)
Columns SESSION_SPID (int)
SESSION_ID (string)
SESSION_COMMAND_COUNT (int)
OBJECT_PARENT_PATH (string)
OBJECT_ID (string)
OBJECT_VERSION (int)
OBJECT_DATA_VERSION (int)
OBJECT_CPU_TIME_MS (long)
OBJECT_READS (long)
OBJECT_READ_KB (long)
OBJECT_WRITES (long)
OBJECT_WRITE_KB (long)
OBJECT_ROWS_SCANNED (long)
OBJECT_ROWS_RETURNED (long)

for more details see: http://msdn2.microsoft.com/en-us/library/bb934100(SQL.100).aspx

The first time I ran this I thought it was not working as I got the following.

image

However, in Books Online it has the following description of this rowset (italics mine)

Provides resource usage and activity information about the objects in use by the current executing commands on the server.

And sure enough when you execute a long running query and query the rowset while the other query is still running you will get results like the following.

image

So this rowset is actually reporting of in-memory structures, while commands are executing which gives

Name DISCOVER_OBJECT_ACTIVITY
Restrictions OBJECT_PARENT_PATH  (string)
OBJECT_ID (string)
Columns OBJECT_PARENT_PATH (string)
OBJECT_ID (string)
OBJECT_CPU_TIME_MS (long)
OBJECT_READS (long)
OBJECT_READ_KB (long)
OBJECT_WRITES (long)
OBJECT_WRITE_KB (long)
OBJECT_AGGREGATION_HIT (long)
OBJECT_AGGREGATION_MISS (long)
OBJECT_HIT (long)
OBJECT_MISS (long)
OBJECT_VERSION (int)
OBJECT_DATA_VERSION (int)
OBJECT_ROWS_SCANNED (long)
OBJECT_ROWS_RETURNED (long)

Provides resource usage per object since the start of the service.

for more details see: http://msdn2.microsoft.com/en-us/library/bb934094(SQL.100).aspx

   image

Name DISCOVER_OBJECT_MEMORY_USAGE
Restrictions OBJECT_PARENT_PATH (string)
OBJECT_ID (string)
Columns OBJECT_PARENT_PATH (string)
OBJECT_ID (string)
OBJECT_MEMORY_SHRINKABLE (long)
OBJECT_MEMORY_NONSHRINKABLE (long)
OBJECT_VERSION (int)
OBJECT_DATA_VERSION (int)
OBJECT_TYPE_ID (int)
OBJECT_TIME_CREATED (dateTime)

Provides information about memory resources used by objects.

for more details see: http://msdn2.microsoft.com/en-us/library/bb934098(SQL.100).aspx

image

author: Darren Gosbell | posted @ Thursday, February 21, 2008 2:56 PM | Feedback (0)

CTP6 and CU6 are now available - Follow-up


Just a quick follow-up on  Chris Webb's recent post about the release of CTP6 for SQL 2008 and CU6 for SQL 2005.

Chris mentions that there is still no suggestion that SP3 for SQL Server 2005 is in the works despite having released 6 cumulative update packs. If you would like to see a Service Pack 3 you can add your voice to the push for SP3 by voting for it on connect here: https://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=326575

The other interesting thing about the release of CTP6 for SQL Server 2008 is that Microsoft is holding a bug bash competition where you can win yourself an XBox. The details for this can be found here:https://connect.microsoft.com/SQLServer/content/content.aspx?ContentID=7734

author: Darren Gosbell | posted @ Thursday, February 21, 2008 1:51 PM | Feedback (0)

Building a better DMV


Following along on my recent theme of exploring the metadata rowsets and the new Dynamic Management Views (DMV) in SSAS 2008. I have added a new DMV function to the Analysis Services Stored Procedure project (www.codeplex.com/ASStoredProcedures) which is included in the v1.2 release that I put out just before Christmas.

This all started off when I decided to look at extending the existing Discover() function to provide for sorting and filtering. And I originally started off by building a wrapper function around the existing Discover() function with the following syntax:

DMV( rowset [, whereClause [, sortOrder ]] ) 

Which was functional, but I still had to add the option of selecting a sub-set of columns and I felt that the syntax was was not as "clean" as I would have liked. It was at this point that Mosha suggested that I could write a parser and pass in a SELECT statement as a string. So from that I have developed the function to have the following signature:

DMV( query [, restrictions] )

Which lets us do things like the following to get a list of cubes:

CALL ASSP.DMV("
  SELECT CUBE_NAME , BASE_CUBE_NAME  
  FROM $system.MDSCHEMA_CUBES
  WHERE CUBE_SOURCE = 1
");

The query in the example above came from a blog post by Vidas where he was running through the new DMV functionality in SSAS 2008. You can copy and paste any of the queries in that post and run them against SSAS 2005 using the ASSP.DMV() function.

So, how does this DMV function compare to DMV's in SSAS 2008? Well, below are two lists, the first is a list of the limitations that the SSAS 2008 DMVs currently have as of the November 2007 CTP (CTP5).

  • COUNT, SUM (and other aggregate functions) do not work (you cannot create your own derived columns)
  • string functions like LEFT do not work
  • ORDER BY <number> does not ORDER
  • JOINS appear not to work
  • SELECT DISTINCT does not work
  • ORDER BY clause only allows one field to order by. Adding second field raises error: “Error (Data mining): Only one order expression is allowed for TOP expression at line 1, column 1″
  • the LIKE operator does not work in the WHERE clause.

Rather than extending the SQL support in MDX, the SSAS team chose to exploit the SQL subset that was supported by DMX, this vastly reduced the amount of effort required to implement the DMV feature, but it did come with the above limitations.

The following list are the limitations of the SSAS 2008 DMVs (as of CTP5) that ASSP.DMV() overcomes.

  • SELECT DISTINCT works, only returning distinct rows
  • ORDER BY clause accepts multiple fields
  • the LIKE operator works in the WHERE clause
  • The $System schema is optional (the fact that you are calling the DMV function implies this anyway, so this is not such a big issue)

In addition to these points ASSP.DMV() allows you to execute queries against rowsets that have mandatory restrictions which overcomes the issue in SSAS 2008 that was reported here http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2477175&SiteID=1

ASSP.DMV() has two mechanisms for doing this. Firstly it has simple restriction matching, so if you have a query with filter conditions that repeat the following pattern "<COLUMN> = <VALUE>" and are linked with logical ANDs then the DMV function can derive the restriction. Any deviation from this pattern, including the use of brackets will cause this matching to fail.

However, if you wish to use a more complicated filter you could still do so by manually passing in the restrictions XML yourself using the optional second parameter of the ASSP.DMV() function. (Note that the restrictions would be applied first and then any filtering in the WHERE clause would be applied after that)

So with ASSP.DMV() you can execute the following:

-- This call demonstrates how ASSP.DMV can map WHERE filters to the restrictions 
--
of the XML/A discover call in order to support querying rowsets that have
--
required restrictions.

CALL ASSP.DMV("
SELECT *
FROM $SYSTEM.DISCOVER_PARTITION_DIMENSION_STAT
WHERE DATABASE_NAME = 'Adventure Works DW'
  AND CUBE_NAME = 'Adventure Works'
  AND MEASURE_GROUP_NAME = 'Internet Sales'
 
  AND PARTITION_NAME = 'Internet_Sales_2003'
ORDER BY DIMENSION_NAME, ATTRIBUTE_COUNT_MAX desc
");

Which produces the following output:

image

Or you can execute it passing in the restrictions XML explicitly (which is what ASSP.DMV infers from the WHERE clause in the call above):

-- this is the same as the call above, just with an explicit restrictions clause
-- rather than using an implied one from the WHERE conditions.
CALL ASSP.DMV("
SELECT *
FROM $SYSTEM.DISCOVER_PARTITION_DIMENSION_STAT
ORDER BY DIMENSION_NAME, ATTRIBUTE_COUNT_MAX desc"
,"<DATABASE_NAME>Adventure Works DW</DATABASE_NAME>
<CUBE_NAME>Adventure Works</CUBE_NAME>
<MEASURE_GROUP_NAME>Internet Sales</MEASURE_GROUP_NAME>
<PARTITION_NAME>Internet_Sales_2003</PARTITION_NAME>
");
image 

The fact that SSAS 2008 DMVs do not work with rowsets that have mandatory restrictions leads me to believe that they have been implemented in such a way that they always do an unrestricted XMLA discover call. Which means that it must be storing the entire XMLA result in memory and then processing the WHERE and ORDER BY clauses. In contrast, the ASSP.DMV() function will always attempt to do restriction matching of any filter conditions for every query (if the where clause is appropriate) which will mean that the SSAS server will often be sending back less data which should help performance and memory usage.

Because you have to pass in your query as a string with ASSP.DMV(), it obviously does not support syntax highlighting, however it does support all MDX comment types and you can use white space to lay out your query to your liking. (Below is an example where I have highlighted the comments in green to make them easier to see)

CALL ASSP.DMV("
SELECT CUBE_NAME 
, BASE_CUBE_NAME // the DMV function suppports all the MDX comment types FROM $system.MDSCHEMA_CUBES /* the $system schema is optional */ WHERE CUBE_SOURCE = 1 -- Just cubes, 2 - dimensions. ");

If you wanted to get a list of all dimensions that relate to a given measure group then you could do so with a query like the following:

Call ASSP.DMV("
SELECT CUBE_NAME
  , MEASUREGROUP_NAME
  , [DIMENSION_UNIQUE_NAME]
  , DIMENSION_IS_VISIBLE
  , DIMENSION_GRANULARITY
FROM $SYSTEM.MDSCHEMA_MEASUREGROUP_DIMENSIONS
WHERE CUBE_NAME = ‘Direct Sales’
  AND MEASUREGROUP_NAME = ‘Internet Customers’");

image

In order to demonstrate a couple of features at once, let's suppose that you want to get a list of all dimensions for any cube that contains the word "Sales" in it's name and we want to sort the list of dimensions by the dimension ordinal and then by the the cube name in descending order. This is not possible with the native DMV functionality in SSAS 2008 (as of the Nov 2007 CTP), but with ASSP.DMV you can execute the following:

Call ASSP.DMV("
SELECT DIMENSION_ORDINAL
  , DIMENSION_NAME
  , DIMENSION_CARDINALITY
  , DEFAULT_HIERARCHY
  , CUBE_NAME
FROM MDSCHEMA_DIMENSIONS
WHERE CUBE_NAME LIKE '%Sales%'
ORDER BY DIMENSION_ORDINAL, CUBE_NAME desc
");

I have highlighted the two columns in the ORDER BY clause in the following output:

image

In order to find out what sort of data is available, running either of the following will give you a list of all the available rowsets which you can query using ASSP.DMV()

CALL ASSP.DiscoverRowsets();

CALL ASSP.DMV("SELECT * FROM DISCOVER_SCHEMA_ROWSETS");

(with the exception of DISCOVER_XML_METADATA which returns a hierarchical results set, but you can get at that using ASSP.DiscoverXmlMetadata() function)

A Note on Querying Data

Also it should be noted that the last two examples Vidas listed in his post will actually currently work against SSAS 2005, the only trouble is that these sorts of queries can only be done by Administrators, so you can't really use them in anything that standard users will interact with. I have not double checked if this same restriction applies in 2008, but I would expect that it does.

SELECT * FROM [$Account].[$Account] ;

SELECT * FROM [Direct Sales].[Exchange Rates];

In fact you should find that you can get at most of the tables that you can see in the following rowset if you are an administrator.

CALL ASSP.DMV("SELECT * FROM DBSCHEMA_TABLES");

Technorati Tags: , , , ,

author: Darren Gosbell | posted @ Wednesday, January 30, 2008 10:45 PM | Feedback (0)

SSAS: Acquiring Locks using XML/A from SSMS - Part 2


Mosha commented on my last post on this topic that there was another simpler way of doing the same thing from SSMS. And that is to execute the following commands from an MDX window.

First run ...

<BeginTransaction xmlns="http://schemas.microsoft.com/analysisservices/2003/engine" />

and then...

<Lock xmlns="http://schemas.microsoft.com/analysisservices/2003/engine">
   <ID>496CEC1F-D66A-4C80-9E14-305D6CF41D86</ID>
   <Object>
     <DatabaseID>Adventure Works DW</DatabaseID>
   </Object>
   <Mode>CommitShared</Mode>
</Lock>

You will not get the nice syntax highlighting when using an MDX window, but it does still know how to run XML/A.

I probably should have figured this out myself as I pointed out last year that you can run XMLA commands from an MDX window and I mentioned in the last post that unlike XMLA windows which open a new session per command, MDX windows execute their commands on the same session. But at the time I was focusing more on showing some of the detail of the XML/A messages that are sent to SSAS than on finding the simplest approach.

But to take this even further you can enter the following into an MDX window and run the whole lot in one go.

BEGIN TRAN

GO 

<Lock xmlns="http://schemas.microsoft.com/analysisservices/2003/engine">
   <ID>496CEC1F-D66A-4C80-9E14-305D6CF41D86</ID>
   <Object>
     <DatabaseID>Adventure Works DW</DatabaseID>
   </Object>
   <Mode>CommitShared</Mode>
</Lock>

GO 

ROLLBACK TRAN

This exploits the GO statement, which is not really MDX, it is actually not even sent to the SSAS server, but SSMS treats it as a command delimiter. Obviously the above sample does not do much as it simply starts a transaction, takes a lock and the rolls back the transaction, but you could put in as many other commands or queries (separated by GO statements) as you liked after taking the lock.

Enjoy :)

Technorati Tags: ,

author: Darren Gosbell | posted @ Thursday, January 24, 2008 4:07 AM | Feedback (0)

A Performance Point Breakfast Seminar in Brisbane


If you are interested in learning a bit more about Microsoft's new Performance Point Server 2007 and you will be in Brisbane on the 12th of February, then head on over to the Microsoft Offices where Peter from Intellimax Solutions is giving a breakfast seminar. He will be doing a full run through of the product from a practical perspective. You can get more information here: http://www.intellimaxsolutions.com.au/performancepointserver.html

author: Darren Gosbell | posted @ Monday, January 14, 2008 7:32 AM | Feedback (0)

SSAS: Acquiring Locks using XML/A from SSMS


I put the following code sample together in response to this question on the Analysis Services forum.

If you read Books Online, you might think that running the following statement in SSMS would work :

<Lock xmlns="http://schemas.microsoft.com/analysisservices/2003/engine">
   <
ID>496CEC1F-D66A-4C80-9E14-305D6CF41D86</ID>
   <
Object>
     <
DatabaseID>Adventure Works DW</DatabaseID>
   </
Object>
   <
Mode>CommitShared</Mode>
</
Lock
>

But it will throw the following error:

Executing the query ...
Transaction errors: The request requires an active transaction.
Execution complete

Executing ...

<BeginTransaction xmlns="http://schemas.microsoft.com/analysisservices/2003/engine" />

... before hand does not help as time you run an XMLA statement from SSMS is that it executes in a new session, so the lock command gets executed on a different session to the one where the transaction was started. From looking at a profiler trace it appears that when you run a command from SSMS it opens a connection, executes the command and then closes the connection. (In contrast an MDX window appears to run all statements in the one session)

Now I don't actually think that there should be too many circumstances where you need to use an explicit lock. Individual commands will take their own locks anyway.  But what I did think would of interest, and is the reason I am posting this sample, is that it demonstrates how to execute a series of commands against a single session within a transaction from SSMS.

This involves constructing the entire SOAP envelope and I could not really find a lot of other samples around on how to do this. Using locking as an example provides a simple way of demonstrating this as we know that a lock can only be acquired inside a transaction.

Start off by running the following code to create a transaction :
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
  <Header>
    <BeginSession xmlns="urn:schemas-microsoft-com:xml-analysis"/>
  </Header>
  <Body>
    <Execute xmlns="urn:schemas-microsoft-com:xml-analysis">
      <Command>
        <BeginTransaction xmlns="http://schemas.microsoft.com/analysisservices/2003/engine">
</BeginTransaction> </Command> <Properties> <PropertyList> <LocaleIdentifier>1033</LocaleIdentifier> </PropertyList> </Properties> </Execute> </Body> </Envelope>

The xmla it returns will contain a SessionID, you will need to replace the SessionID in yellow below with the one returned from the first statement in order for the next statements to work. The GUID in green is one I generated by running "SELECT newid()" against my SQL Server, you could generate your own in the same way or use the Tools - Create GUID option in VS.Net.

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
  <Header>
    <Session xmlns="urn:schemas-microsoft-com:xml-analysis"
SessionId="C63C5C2B-7401-4127-8A03-D05F3D87ACBA" /> </Header> <Body> <Execute xmlns="urn:schemas-microsoft-com:xml-analysis"> <Command> <Lock xmlns="http://schemas.microsoft.com/analysisservices/2003/engine"> <ID>496CEC1F-D66A-4C80-9E14-305D6CF41D86</ID> <Object> <DatabaseID>Adventure Works DW</DatabaseID> </Object> <Mode>CommitShared</Mode> </Lock> </Command> <Properties> <PropertyList> <LocaleIdentifier>1033</LocaleIdentifier> </PropertyList> </Properties> </Execute> </Body> </Envelope>

At this point you can execute any other statements you like on the same session. When you are finished finished you can call unlock ( I believe that committing or rollingback the transaction will also release the lock)

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
  <Header>
    <Session xmlns="urn:schemas-microsoft-com:xml-analysis" 
SessionId="C63C5C2B-7401-4127-8A03-D05F3D87ACBA" /> </Header> <Body> <Execute xmlns="urn:schemas-microsoft-com:xml-analysis"> <Command> <Unlock xmlns="http://schemas.microsoft.com/analysisservices/2003/engine"> <ID>496CEC1F-D66A-4C80-9E14-305D6CF41D86</ID> </Unlock> </Command> <Properties> <PropertyList> <LocaleIdentifier>1033</LocaleIdentifier> </PropertyList> </Properties> </Execute> </Body> </Envelope>

And finally you need to either rollback or commit the transaction.

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
  <Header>
    <Session xmlns="urn:schemas-microsoft-com:xml-analysis"
SessionId="C63C5C2B-7401-4127-8A03-D05F3D87ACBA" /> </Header> <Body> <Execute xmlns="urn:schemas-microsoft-com:xml-analysis"> <Command> <RollbackTransaction xmlns="http://schemas.microsoft.com/analysisservices/2003/engine" /> </Command> <Properties> <PropertyList> <LocaleIdentifier>1033</LocaleIdentifier> </PropertyList> </Properties> </Execute> </Body> </Envelope>

author: Darren Gosbell | posted @ Sunday, January 13, 2008 11:28 PM | Feedback (1)

PowerSSAS - Exporting information to Excel


There was a comment recently on the post where I annouced the release of powerSSAS, asking if you could export the information from the PowerShell Analyzer Provider Explorer. While I don't think you can do it from the Provider Explorer, but you can use the Export-Csv cmdlet that is built-in to PowerShell. Something like the following would export a list of dimensions to a .csv file.

add-PSSnapin powerssas
new-PSDrive ssas powerssas localhost
cd ssas:
cd "Databases\Adventure Works DW\Dimensions"
# this lists out all the properties
dir | export-Csv c:\dims.csv 

And if you only wanted certain properties, you could change the last line to something like the following, using the select-object cmdlet (aliased as "select" in this example) to only return the specified properties:

dir | Select Name,State,LastSchemaUpdate,LastProcessed | export-Csv c:\dims2.csv 

If you really have special requirements, or if you simply need to use a delimiter other than a comma (eg. if your region uses commas for the decimal separator) then you could roll your own string and redirect that to a file.

add-PSSnapin powerssas
new-PSDrive ssas powerssas localhost
cd ssas:
cd "Databases\Adventure Works DW\Dimensions"
# loop through the dimensions and 
# redirect the constructed string to a file
$( 
  foreach ($d in get-ChildItem)
  {
    "$($d.Name)`t$($d.State)"
  } 
) > c:\data\dims4.txt 

Note that Powershell uses the backtick (`) as an escape character, so the `t is interpreted as a tab character and I am collecting the whole loop up inside an expression $(...) and redirecting that through to a file.

Finally if you wanted absolute control over the output, you could instantiate a copy of Excel and control it using the COM object model.

$objXL = New-Object -comobject Excel.Application

But there are issues with that approach, not the least of which is the fact that it does not work on my laptop! Blowing up with the unhelpful error:

"Exception calling "open" with "1" arguement(s): "Old format or
invalid type library. <Exception from HRESULT: 0x80028018 <Type_E_INVDATAREAD>>"

This is possibly because I am using a regional setting of en-AU, not en-US. As googling for this issue turned up the following KB article which was useful - http://support.microsoft.com/kb/320369.

Using the suggested InvokeMember work around looked promising and I did manage to get Excel working to a degree, but I am not going to bother posting that code here as it was pretty messy. And I could not get the second suggested work around of changing the culture of the current thread to work at all from PowerShell.

author: Darren Gosbell | posted @ Monday, January 07, 2008 2:56 PM | Feedback (0)