Showing posts with label Analysis Services SSAS. Show all posts
Showing posts with label Analysis Services SSAS. Show all posts

SQL Server Denali: Analysis Services Roadmap update

If you are a SQL Server BI developers and are amongst the folks eagerly awaiting the release, news and roadmap update on SQL Server Denali Analysis Services, then I would strongly recommend you to read this post by T.K Anand, the Principal Group Program Manager of the SQL Server Analysis Team.

In his post Analysis Services – Vision & Roadmap Update, Anand has provided an update on the SQL Server Analysis Services roadmap released in November 2010 last year. The Business Intelligence Semantic Model is being introduced in Analysis Services for SQL Server “Denali” to build on the strengths and success of Analysis Services, expand its reach to a much broader community, and enable the next generation of BI applications.

Anand quotes “The BI Semantic Model is one model for all end user experiences – reporting, analytics, scorecards, dashboards, and custom applications.  All client tools in the Microsoft BI stack – Excel, PowerPivot, SharePoint Insights and Reporting Services (including Crescent) – operate on this model

To be notified of the next Denali CTP launch, check here http://www.sqlserverlaunch.com/

Exploring New Features of SQL Server 2008 R2 with Excel 2010

SQL Server 2008 R2 November CTP is released and can be downloaded from here .

There is a lot of new functionality available with Business Intelligence with SQL Server 2008 R2. In this article, we will use Excel 2010 as a client for SQL Server Analysis Services to explore some new features.

To get started, we first need to install SQL server 2008 R2 and later Office 2010 to get the Gemini tab. ‘Gemini’ is the official name for SQL Server PowerPivot for Excel and SharePoint.

We will discuss creating pivot table with Excel 2010 using PowerPivot (working with SQL Server 2010 November CTP). After installing Office 2010 and PowerPivot, we get the following tab in Excel 2010.

image

I have used the sample database for SQL Server 2008 R2 November CTP available on codeplex

After clicking on PowerPivot window, select a SQL Server database and use database AdventureWorksDW2008R2.

From Table Import Wizard, select 3 tables namely DimDate, DimSalesTerritory and FactInternetSales as follows

image

You can even give them friendly names as shown. You will get following the Success screen after you click Finish.

image

The data is as shown

image

You can click on Pivot table and create a complete Pivot table.

image

Following table shows Countries in columns, months in rows and sum of Order quantity for calendar years 2007 and 2008.

I added a vertical slicer for SalesTerritoryGroup and the result is as shown:

image

Finally, I also added a PivotChart as shown below:

image

Creating a Cube and Dimension using SSAS

In my previous article, we took an Overview of SSAS and its various components. In this article, we will create a cube by using Business Intelligence Development Studio (BIDS).

We will continue with the example we discussed with SQL Server Integration Services where we created a database with the help of Import Export wizard

1. In the existing solution named DemoBeginning, add an Analysis Services Project and name it DemoCube.

2. In the Solution Explorer, right click on Data Sources and click on New Data Source. This action launches the Data Source Wizard. Connect to the database created earlier named DemoDB. Select the impersonation information as ‘Use the Service Account’. Keep the default name and click Finish.

3. In the Solution Explorer, right click on Data Source View (DSV) and select New Data Source. Select 3 tables from the Production schema and SalesOrderDetail from Sales schema. We will fetch the last table in a short while. Keep the default name for DSV and click Finish. In this DSV you will see that no tables are related to each other.

Note we did not fetch any keys or relationships from the original database to the newly created database.

4. We will create the logical relationships in the DSV. Right click on each table from the Production schema and make ProductID, ProductSubCategoryID and ProductCategoryID as logical primary keys. To create relationships, drag ProductSubCategoryID from Product table and drop on ProductSubCategory. Verify the relationship is as follows:

clip_image002


Similarly drag ProductCategoryID from ProductSubCategory table and drop it on ProductCategory table. Drag ProductID from SalesOrderDetail and create the relationship by dropping it on ProductID of Product table. We have now created primary keys in dimension tables and subsequent foreign keys in fact tables.

5. Dimensions for cube can be created in 2 ways. One is by right clicking on Dimension in Solution Explorer and the other when we create a cube, the dimensions automatically get created. Right click on the cube in Solution Explorer and start the cube wizard. Select existing tables from the ‘Select Creation Method’ screen. Click Next.

clip_image004

6. Click on the Suggest button from the next screen and you will see that the table named SalesOrderDetails is picked as the table with measures. This suggestion is based on the relationship we created in the previous step. Click Next.

7. Select Order Qty, Unit Price and Line Total as the only measures and click Next

8. Click Next, enter the name DemoCube and click Finish. The result will be as shown below.

clip_image006

You can now see the relationship which we created.

9. Right click on the project and select properties. Click Deployment tab and enter your server name. For default instance you can keep it as localhost; for named instance it will be <machine name>\<instance name>. Select Build, Deploy DemoCube.

10. After successful deployment ,select the Browser tab for cube. If you drag and drop Unit Price, Line Total to the data area and Product category Id to the row area, you will see the result as follows.

clip_image008

This result shows category id which is not very user friendly, it will be better if we see the name instead.

11. Go to solution explorer and double click on Product dimension.

Go to properties for Product Id and change NameColumn property as shown below

clip_image010

Similarly change the NameColumn for Product Subcategory ID and Product Category Id.

Create a hierarchy named Product Category as follows:

clip_image012

We need to process the dimension with the changed properties. Now the previous result is as shown below

clip_image014

12. We can format the Line Total by selecting Cube structure tab, select Line Total, select properties and specify FormatString as currency with the following result:

clip_image016

13. We get a warning for the dimension as “Avoid visible attribute hierarchies for attributes used as levels in user-defined hierarchies”. To implement this best practice, select each attributes and change its AttributeHierarchyVisible property to False.

14. Let us add one more dimension for Time. Select DSV, right click on the empty area and select Add/Remove tables, add SalesOrderHeader and click. Specify SalesOrderId from SalesOrderDetails as logical primary key and establish relationship to the newly added table.

15. Right click on Dimensions in solution explorer and add a new dimension. Select ‘Generate a Time Table’ on server, and enter details as shown below:

clip_image018

Click Next, Next again and finally Finish. Verify that the wizard automatically creates one hierarchy with year, quarter, month and Data as attributes

16. We need to add this dimension to the cube, so select the cube structure tab, right click on the empty area for dimensions and select add a cube dimension and select Time. Process and deploy the changes.

17. Go to Browser tab, click reconnect and you will see the newly created dimension added.

clip_image020

18. We can filter the data in following manner

clip_image022

In next article, we will discuss an overview of SQL Server Reporting Services and create a report based on this cube.

Overview of SQL Server Analysis Services (SSAS)

In a previous article, we discussed SQL Server Integration Services, its components and how to create package by using Import Export Wizard. In this article we will take an overview of SSAS and its various components.

With SSIS, we discussed how data can be fetched and cleansed before it is put into a uniform platform. With SSAS, as the name suggests, we can use the data for analysis.

With any organization we have two kinds of data, one which stores transactional details and the other which has historical (Data Warehouse - DW) details. The data in DW will be populated periodically from the data from transactional (OLTP – OnLine Transactional Processing). The data in OLTP is optimized for faster insertion and updations, but the data in DW is meant for faster querying. With DW we can store duplicate data, computed columns, non normalized data as the storage is not important. The important aspect is that when a query is fired, it should give faster results.

When we want to take any decisions based on data, the data needs to be in an analyzed format. For e.g. we want to find out the code churn for developers in an organization. We need to have all the details for each developer; such as how many lines of code were written, modified or deleted. When we need the report, if we start calculating the numbers for each developer for a day, a week, a month or a quarter, it will be time consuming job. If I already have this aggregated data in cache, my job will be very easy. This aggregated data is put in cubes which are termed as multidimensional databases. As the normal cube has multiple dimensions, the data can be viewed with various axes. In this case one axis can be for each developer; another can be for a particular period and third can be for a particular department or for a particular project. Thus we can create different slices for looking at the same data. These slices are termed as dimensions and the actual data is the fact. The sales related data (like the sales person information, data for sales, quantity, and price) will be termed as fact and the axes (like sales person wise information, location wise details, quarterly details) will be termed as dimensions.

The thumb rule says that facts are numeric measures while dimension has primary keys. Thus primary keys in dimension tables are related to foreign keys in fact tables so as to get the details as and when required. There are 2 dimensional models namely star schema and snowflake schema. In star schema, single fact table is related to multiple dimension tables based on primary key in dimension tables (which resembles start like shape) as follows.

image

In snowflake schema, one of the dimension acts as a fact for another dimension thus giving snowflake like appearance as below

image

With SSAS, we first need to specify the source from which data is to be fetched. Later the data can be analyzed. After the source, we can create a Data Source View (DSV) to the source. If the source database has 50 tables, we need not want to work with all of them in a given situation. So we create a window to the data with required tables in it. We can also group tables from different sources and put it in the same DSV.

We can even create logical relationships amongst the table in the DSV where the source database remains intact. We are just creating a logical view to the tables.

In this article, we took an overview of SSAS and its various components. In the next article, we will create a cube by using Business Intelligence Development Studio (BIDS).

Exploring Additional Multi Dimensional Expression (MDX) Functions in SQL Server Analysis Services – Part 3

I recently wrote two articles on MDX in SQL Server Analysis Services

Exploring MDX (Multi Dimensional Expression) in SQL Server Analysis Services

Exploring Additional Multi Dimensional Expression (MDX) Functions in SQL Server Analysis Services – Part 2

This is the third and final part of the series and we will explore some additional functions of MDX:

1. IIF functions
returns one of the possible two values in a logical test. It can return either a string or numeric value.

WITH MEMBER MEASURES.[IIF Value] AS
IIF([Measures].[Order Quantity]>1000
, "Qty High", "Qty Low")
SELECT {[Measures].[Order Quantity],MEASURES.[IIF Value]} ON 0,
[Order Date].[Calendar].[Calendar Quarter].MEMBERS ON 1
FROM DemoCube

In this query, we are displaying whether the quantity is low or high, according to the value for Order Quantity.

image
We can change the condition if required.

2. Let us use IIF with string data

WITH MEMBER [IIF String] AS
IIF([Product].[Product-Category]=Bikes
, "Common", "not common")
SELECT {[Measures].[Sales Amount],[IIF String]} ON 0,
[Product].[Product-Category].[Product Category Key].MEMBERS on 1
FROM DemoCube

3. Let us add a calculated member. The following query creates percent by using MDX and displays results:

WITH MEMBER Measures.[Percent] AS
([Measures].[Sales Amount],[Product].[Product-Category].CurrentMember)
/([Measures].[Sales Amount],[Product].[Product-Category].[All]),
FORMAT_STRING="Percent"
SELECT Measures.[Percent] on 0,
NON EMPTY [Product].[Product-Category].[Product Category Key].members on 1
from democube

This calculation can be kept for further usage if it is added from BIDS (Business Intelligence Development Studio) as follows

image

Here we can give the format string to percent, if required. Later it can directly be used in MDX as follows:

SELECT [Measures].[Percent Sales] ON0,
NON EMPTY [Product].[Product-Category].[Product Category Key].members on1
fromdemocube

This will give the same result as the previous query, with the difference that we already have a calculated field.

4. Let us discuss different ways of using the COUNT function. The following query returns the number of hierarchies in a cube:

WITH MEMBER MEASURES.[Count] AS
dimensions.count
SELECT Measures.[Count] ON 0
FROM DemoCube

5. The following query returns number of levels in the product categories:

WITH MEMBER measures.[Count] AS
[Product].[Product-Category].Levels.Count
Select Measures.[Count] ON 0
FROM DemoCube

6. The following query counts the number of cells in a set of members that consists of children of a category:

WITH MEMBER measures.X AS
[Product].[Product Category Key].children.count
SELECT Measures.X ON 0
FROM DemoCube

We can exclude non empty cells in the following manner:

WITH MEMBER measures.[Count] AS
Count
([Product].[Product Category Key].children, EXCLUDEEMPTY)
SELECT Measures.[Count] ON 0
FROM DemoCube

7. Let us look at parallel period now.

In order to display the data in a more meaningful manner, I have added the following calculated fields in DSV (Data Source View):

Calendar Quarter Description: 'Q' + CONVERT(CHAR (1), CalendarQuarter) +' '+ 'CY ' +
CONVERT(CHAR (4), CalendarYear)


Calendar year description: 'CY '+ CONVERT(CHAR (4), CalendarYear)
and MonthName: EnglishMonthName+' '+ CONVERT(CHAR (4), CalendarYear)

I changed the time dimension to incorporate these changes as follows:

Attribute English Month Name

Key columns collection is as shown below:

image

with Name column pointing to newly created MonthName

Similarly Calendar Quarter key columns is as follows

image

with the Name column pointing to a Calendar Quarter description.

Calendar year’s name column points to Calendar Year Description

A parallel period returns a member from prior period in the same relative position as a specified member.

Two more properties need to be changed for English month name as follows:

OrderBy: AttributeKey
OrderByAttribute: Month Number of year

If we forget these settings, the order will be on character month and which will be misleading/

The following query returns parallel period for the month of July 2002 with a lag of 2 periods based on quarter level which is January 2002

SELECT ParallelPeriod ([Order Date].[Calendar].[Calendar Quarter]
, 2, [Order Date].[Calendar].[English Month Name].&[2002]&[July])
ON 0
FROM DemoCube

A similarly query

SELECT ParallelPeriod ([Order Date].[Calendar].[Calendar Year]
, 1, [Order Date].[Calendar].[Calendar Quarter].&[2002]&[2])
ON 0
FROM DemoCube

gives us result as Quarter 4 and year 2001. If we change the lag to 2 periods, we may not get the result in this case, as the starting year for the data is 2001

8. Parallel period is similar to Cousin function so let us discuss Cousin function.

This function returns child member with same relative position under a parent member as the given child member.

The following query displays cousin of 3rd quarter of year 2001 based on year level in 2002, which gives quarter 1 in 2003

SELECT Cousin
( [Order Date].[Calendar].[Calendar Quarter].&[2001]&[3]
,[Order Date].[Calendar].[Calendar Year].&[2002]
) ON 0
FROM DemoCube

In these 3 articles, we discussed what is MDX, how queries can be used to view data from multi dimensional cubes and different clauses for MDX queries. We also discussed some functions available with MDX (sum, avg, prevmember, count,min, max, topcount) and how formatting can be provided, so that the result looks user friendly. We discussed how calculated members can be created and displayed. We also discussed some more functions like IIF, Parallel Period and Cousin.

Exploring Additional Multi Dimensional Expression (MDX) Functions in SQL Server Analysis Services – Part 2

SQL Server Analysis Services - Additional functions in MDX

In my previous article Exploring MDX (Multi Dimensional Expression) in SQL Server Analysis Services, we discussed how to write simple queries with MDX to query multi dimensional cubes. In this article, we will continue with the same example and add some more functions.

We will also discuss how to format the data when using MDX.

1. Let us see how to format the data

WITH MEMBER Measures.[Order Quantity Formatted] AS
([Measures].[Order Quantity] ),FORMAT_STRING="#,##,#.00"
SELECT NON EMPTY {Measures.[Order Quantity Formatted]
} ON COLUMNS,
NON EMPTY [Product].[Product Category Key].MEMBERS on ROWS
FROM
DemoCube

The result looks as follows:

image

If we change the format string to ="#,##,#.##” the result is as follows

image

2. Let us add a time dimension to the cube. Open the previously existing DSV (Data Source View). Right click on the empty area and select Add/Remove tables > add DimTime tab. Give the friendly name as ‘Time’.

3. Right click on Dimensions in Solution Explorer > select New Dimension and select the radio button for Use Existing Tables. In Specify Source Information dialog, select as shown below:

image

Keep the name for the dimension as Time and click Finish

4. Double click on the cube in Solution Explorer and select Cube Structure tab. Right click on the empty area for Dimensions and add Time dimension as a new Cube Dimension. We see that all the three dates - ship date, order date and due date are added.

5. Let us add more attributes to this dimension. Select Time Dimension from the Solution Explorer > drag and drop EnglishMonthName, CalendarQuarter, CalendarYear from Data Source View into attributes.

6. Create a hierarchy named Calendar with Calendar Year - Calendar Quarter – English Month name – Time Key. Change the name column for the Time Key to FullAlternateDateKey and build and deploy. If we browse in BIDS we see data as shown

image

7. Let us use this in a MDX query now.

SELECT [Measures].[Order Quantity] ON 0,
TOPCOUNT ([Order Date].[Time
Key].MEMBERS,5,[Measures].[Order Quantity]) ON 1
FROM DemoCube

will give the result as shown below:

image

8. Now let us use the WHERE clause. The WHERE clause determines which dimension or member is to be used as a slicer. A query can have multiple axes, but when a query has 3 axes we will not be able to display it (in SSMS). For this the WHERE clause has been provided.

SELECT [Measures].[Order Quantity] on COLUMNS,
[Product].[Subcategory].[Product Subcategory Key].&[2]
on rows
FROM
DemoCube
WHERE ([Order Date].[Calendar].[Time Key].[2001-08-01 00:00:00.000] )
We can also have more than one member in WHERE
SELECT [Measures].[Order Quantity] on COLUMNS,
[Product].[Subcategory].[Product Subcategory Key].&[2] on rows
FROM
DemoCube
WHERE ([Order Date].[Calendar Year].&[2001],
[Order Date].[English Month Name].&[August])

9. Let us see how certain functions can be used in calculated members

WITH MEMBER Measures.Total AS SUM
( { [Order Date].[Calendar].[Calendar Year].&[2001]
, [Order Date].[Calendar].[Calendar Year].&[2003]},
[Measures].[Sales Amount])
SELECT Measures.Total ON 0
,NON EMPTY [Product].[Product-Category].[Product Category Key].Members ON 1
FROM DemoCube

In this query we are using function SUM where sum of two years 2001 and 2003 for Sales amount is calculated. The result is as shown.

image

Similarly we can find average by using AVG function.

10. Following query returns maximum quarterly sales for each category

WITH MEMBER Measures.[Max Value] AS Max
([Order Date].[Calendar].[Calendar Quarter]
, [Measures].[Sales Amount]
)
SELECT Measures.[Max Value] ON 0,
NON EMPTY [Order Date].[Calendar].[Calendar Quarter] *
[Product].[Product-Category].[Product Category Key].MEMBERS
ON 1
FROM DemoCube

Max can be replaced by min if required.

11. Let us see following query which uses ‘PrevMember’ which returns previous member in the same level as the specified member

This query returns 2002 as the previous year for 2003

SELECT
[Order Date].[Calendar].[Calendar Year].&[2003].PrevMember
ON 0
FROM DemoCube

Similarly NextMember can be used.

12. Following query returns user name of the user that is executing the query

WITH MEMBER Measures.[Name] AS UserName
SELECT Measures.[Name] ON COLUMNS
FROM DemoCube

In this article we discussed how the WHERE clause works in MDX. We also discussed some functions like topcount, sum, avg, min, max, prevmember etc.

In next article we will discuss how iif and count functions can be used in MDX. We will also discuss how parallel period works.

Exploring MDX (Multi Dimensional Expression) in SQL Server Analysis Services

Introduction to MDX

Multi Dimensional eXpression (MDX) is a language created to allow the users to work with multi dimensional data in Microsoft SQL Server Analysis Services. It works as a query language with OLAP (Online Analytical Processing) cubes. We can also use it to create calculated members or use the various functions provided which can be used to query data.

Differences in SQL and MDX

If you are a little familiar with SQL (Structured Query Language), you may feel there is some similarity with MDX in some aspects. The SELECT clause, FROM clause and WHILE clause are similar with SQL and MDX. MDX provides various ways with which a cube can be queried - it provides different functions.

With MDX we can even create, modify and delete cubes if required.

MDX infrastructure

MDX works with multi dimensional data where we mostly have more than 2 dimensions. A dimension is a collection of related objects. These objects are called as attributes which provide information about fact data. For example when we consider attributes for a customer dimension they can be customer name, type of customer (low, ordinary or priority), location for customer etc. If we need detailed information like – ‘in a specific month all the transactions for a particular customer’ ,we need the details coming from the fact tables. Cubes contain different dimensions on which users base the queries. For example the dimension can be the type of customer, location of customer or time period.

How MDX queries data

To identify data, MDX uses reference system. This reference system is based on tuples. A tuple can identify a cell uniquely. Like we reference a cell in a spreadsheet by giving the column followed by the row number, we can also reference the tuple. The similarity ends here as there can be multiple dimensions in a cube as against to only rows and columns in a spreadsheet. A tuple can also be referenced by considering a default member. This default member is [All] member for every hierarchy in a cube.

We will start by creating a cube on adventureworksDW database.

1. Create Analysis Services project and name it MDXDemo

2. Add a new data source in it, which directs to Adventure Works DW database namely ‘AdventureWorksDW’

3. Add a new Data Source View (named Adventure Works DW) which consists of tables DimProduct, DimProducSubCategory, DimProducCaetgory, DimCustomer and FactInternetSales. Change friendly names for tables to as shown in figure:

clip_image002

4. Create a cube with default settings but selective measures as Order Quantity, Unit Price, Sales Amount and Internet Sales Count. Give cube name as DemoCube.

5. Go to the Properties of the project (MDXDemo) > select Deployment tab and enter server name. (if you are working with default instance, you can keep the name provided by default i.e. localhost)

6. After successful deployment, open SSMS and connect to Analysis Services to work with MDX queries.

7. Create a new query, connect to MDXDemo database

Enter following MDX query:

SELECT FROM DemoCube

In this case default member for Order Quantity is displayed as 60938

Enter the query as

SELECT [Measures].[Order Quantity] ON 0
FROM DemoCube

will also give the same result. (0 can also be replaced by COLUMNS) which is the axis.

8. Let us add one more measure as follows:

clip_image004

In this case we see two measures Order Quantity and Sales Amount on Columns

Let us change the default measure. Go to Cube Structure tab in MDXDemo project in BIDS (Business Intelligence Development Studio). Right click on Sales Amount and click Move Up till it becomes first measure. Redeploy the cube and again enter the query

SELECT FROM DemoCube

Now the value shown is different. Let us format this to show only 2 digits after decimal. Enter the FormatString as #,###,##.## and see the difference now.

The default measure can also be changed by MDX as follows

ALTER CUBE DemoCube
UPDATE DIMENSION
Measures, DEFAULT_MEMBER=[Measures].[Order Quantity]

In this case we are again changing the default measure back to Order Quantity

9. Go to DSV (Data Source View), right click on Customers and add a named calculation with name as FullName and expression as FirstName + ‘ ‘ + LastName. Double click on Customers dimensions and specify FullName as NameColumn (so as to display name and not number).

10. Specify similarly EnglishProductName for Product key, EnglishProductCategoryName for product category key and EnglishProductSubcategoryName for product subcategory key in Products dimension.

11. Let us give query for a specific product category Bikes as follows

SELECT [Product].[Product Category Key].[Bikes]
ON COLUMNS
FROM DemoCube
OR
SELECT
[Product].[Product Category Key].&[1]
ON COLUMNS
FROM DemoCube

12. Let us add rows now (add another axis)

SELECT [Product].[Product Category Key].&[1]
ON COLUMNS,
Customer].[Customer Key].MEMBERS ON ROWS
FROM
DemoCube

With this we get Sales Amount for Bikes for all different customers

This can also be achieved by using ordinals as 0 and 1 instead or COLUMNS and ROWS

13.

SELECT NON EMPTY [Product].[Product Category Key].MEMBERS
ON COLUMNS,
[Customer].[Customer Key].MEMBERS ON ROWS
FROM
DemoCube

to get all categories (we added NON EMPTY to get rid of null values)
The result is as follows:

clip_image006


14. Try following query to see Order Quantity for various categories

SELECT [Measures].[Order Quantity]
ON COLUMNS,
[Product].[Product Category Key].MEMBERS ON ROWS
FROM
DemoCube

15. Let us see how to add a calculated member

WITH MEMBER Measures.[Calculated Member] AS
([Measures].[Order Quantity] * [Measures].[Unit Price])
SELECT NON EMPTY {Measures.[Calculated Member],
[Measures].[Sales Amount]} ON COLUMNS,
NON EMPTY [Product].[Product Category Key].MEMBERS on ROWS
FROM
DemoCube

First we calculate the member and use it with select to view the result

clip_image008

16. If we replace Members after Produt Category Key with CHILDREN we get the result as follows

SELECT NON EMPTY {Measures.[Calculated Member],
[Measures].[Sales Amount]} ON COLUMNS,
NON EMPTY [Product].[Product Category Key].CHILDREN on ROWS
FROM
DemoCube

clip_image010

We get only the (non empty) children for categories, all categories are not displayed.

17. Let us see how to display results in order

For this we will first create of products as follows : Within category, Product sub category and within it product name.

Select Product Dimension, drag and drop Product Category key on hierarchies, drag sub category within it and product key within it, name this hierarchy as Product-Category. Deploy the changes to analysis server.

Let us give query without order

select NON EMPTY
{[Measures].[Order Quantity],[Measures].[Sales Amount]} ON 0,
NON EMPTY DESCENDANTS
([Product].[Product-Category].[Product Subcategory Key].[gloves],
,after)
on 1 from democube

The result looks as follows

clip_image012

With descendants function we can use SELF_AND_AFTER clause instead of AFTER clause to get the current descendants member including the specified member
Now let us give query with Order function:

select NON EMPTY
{[Measures].[Order Quantity],[Measures].[Sales Amount]} ON 0,
NON EMPTY ORDER(
DESCENDANTS
([Product].[Product-Category].[Product Subcategory Key].[gloves],
,after),
[Measures].[Sales Amount]
,asc) on 1
from democube

The result will be orderd on Sales Amout as follows

clip_image014


18. Let us add one more sub category and increase a bit of complexity

select NON EMPTY
{[Measures].[Order Quantity],[Measures].[Sales Amount]}
ON 0,
NON EMPTY ORDER(
{DESCENDANTS
([Product].[Product-Category].[Product Subcategory Key].[gloves],
,after),
DESCENDANTS
([Product].[Product-Category].[Product Subcategory Key].[vests],
,after)},
[Measures].[Sales Amount]
,asc) on 1
from democube

In this case we see the result ordered within a particular sub category, sales amount wise.

clip_image016

The result of SELF_AND_AFTER clause is as follows:

clip_image018

In this we see the ‘gloves’ and’ vests’ also included in the result because of the changed clause in descendants function.

In next article we will talk about some advanced functions from MDX.

Working with SQL Server AMO (Analysis Management Objects) – Part 2

In the first part of this article, we saw how to connect to the analysis server and list all the databases. We then chose a database and displayed the cubes, measures, partitions and dimensions in a ListBox.

In this part of the article, we will see how to take backups of MultiDimensional Anaysis Service Databases, explore the security part of how to add a role, a member to role and how to provide the necessary permissions. We will also see how to create new databases, data sources, data source views and required tables . Lastly I will also demonstrate how AMO offers to wok without connecting to the analysis services instance.

AMO can be very useful for taking backups of multidimensional databases, we can show all the available databases, ask the user to select the one he needs to backup and ask the user to specify device(s) for backup.

With following code we can take backup of any analysis services database and again restore it:

//declare objects
Server server;
Database db=new Database();
//connect to analysis server
server = new Server();
server.Connect(@"data source=<server name>");
db = server.Databases["<database name>"];
//take backup of analysis services database
//the extension is abf
db.Backup(@"<path>");

//restore database
//connect to analysis server
Server server;
server = new Server();
server.Connect(@"data source=<server name>");
//restore database with abf file name and new name for the
//analysis services database
server.Restore(@"<path and abf file name>", "<database name>");

We can use AMO for adding security related objects, like creating roles and later adding users to the roles. This can be a very useful feature where in we can provide the security for analysis services database access on the fly. When we want to revoke permission for a particular we can even remove a particular member from a role or even remove the complete role itself.

The following code explains how to add a role, a member to role and how to provide the necessary permissions

Server server;
try
{
//connect to analysis server
server = new Server();
server.Connect(@"data source=<server name>");
//pick up a particular database
db = server.Databases[0];
Cube cube=db.Cubes[0];
//create a new role
Role role;
role = new Role("<role name>");
//add member to the role
RoleMember member = new RoleMember();
member.Name = @"<domain name>\<user name>";
role.Members.Add(member);
db.Roles.Add(role);
role.Update();
//provide permissions as administrator
DatabasePermission perm;
perm = db.DatabasePermissions.Add(role.ID);
perm.Administer = true;
perm.Update();
//add permission for cube
CubePermission cubeperm;
cubeperm = cube.CubePermissions.Add(role.ID);
cubeperm.Read = ReadAccess.Allowed;
cubeperm.Update();
}
catch (AmoException amoex)
{
MessageBox.Show(amoex.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

We can use AMO to create new database, data sources, data source views and required tables in them.

With following code we will use adventure works as database and choose Person.Contact table into DSV

try
{
//connect to analysis server
server = new Server();
server.Connect(@"data source=<server name>");
//create new database
db = new Database("<database name>");
db = server.Databases.Add("<database name>");
db.Update();
DataSource datasrc = db.DataSources.FindByName("<database name>");
// Create the data source
datasrc = db.DataSources.Add("<data source name>", "<data source id");
datasrc.ConnectionString = @"Provider=SQLNCLI10.1;Data Source=<server name>;
Integrated Security=SSPI;Initial Catalog=adventureworks"
;
// Send the data source definition to the server.
datasrc.Update();
//add Data source view to the newly created data source
DataSourceView dsv = null;
dsv = db.DataSourceViews.Add();
dsv.DataSourceID = "<data source id>";
dsv.Name = "<data source view name>";
dsv.Schema = new DataSet();
System.Data.OleDb.OleDbConnection cn =
new System.Data.OleDb.OleDbConnection(dsv.DataSource.ConnectionString);
cn.Open();
//add table
System.Data.OleDb.OleDbDataAdapter daContact =
new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM Person.Contact", cn);
daContact.FillSchema(dsv.Schema, SchemaType.Mapped, "Contact");
DataTable[] dts =
daContact.FillSchema(dsv.Schema,SchemaType.Mapped, "Contact");
DataTable dt = dts[0];
dt.ExtendedProperties.Add("TableType", "Table");
//this step will specify the schema name for table
dt.ExtendedProperties.Add("DbSchemaName", "Person");
dt.ExtendedProperties.Add("DbTableName", "Contact");
//following step can be ommitted if we want to keep same friendly name
dt.ExtendedProperties.Add("FriendlyName", "Contact");
dsv.Update();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

Another facility which AMO offers is to wok without connecting to the analysis services instance. This metadata can be kept for later usage for deployment.

With following code we process partition, dimensions and cube without actually executing. Later we execute all in one batch in parallel.

try
{
//connect to analysis server
server = new Server();
server.Connect(@"data source=<server name>");
server.CaptureXml = true;
//list all the databases from the server in listbox
db = server.Databases["<database name>"];
foreach (Cube cube in db.Cubes)
{
cube.Process(ProcessType.ProcessFull);
foreach (MeasureGroup measuregrp in cube.MeasureGroups)
{
foreach (Partition partition in measuregrp.Partitions)
{
partition.Process();
}
}
}

foreach (Dimension dim in db.Dimensions)
{
dim.Process();
}
//stop capturing xml
server.CaptureXml = false;
//execute actually on server
XmlaResultCollection results = server.ExecuteCaptureLog(true, true);
foreach (XmlaResult result in results)
{
foreach (XmlaMessage message in result.Messages)
{
MessageBox.Show((message.Description));
if (message is XmlaError)
{

// the processing failed, there is at
//least one error
}
}
}

}
catch (AmoException amoEx)
{
MessageBox.Show(amoEx.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);

}

}

Working with SQL Server AMO (Analysis Management Objects) – Part 1

AMO provides us with a collection of classes with which we can manage objects of a running instance of SQL Server Analysis Services programmatically. These classes are available in namespace Microsoft.AnalysisServices. The assembly is available at the location <drive name>:\Program Files\Microsoft SQL Server\100\SDK\Assemblies

With AMO we can create, modify, delete and view objects like dimensions, cubes, analysis services databases etc. We can also process the cubes, partitions as and when required.

One important thing to remember is you cannot query data with AMO; for this you will need ADOMD.NET.

AMO gives commands from client application and are converted to XMLA (XML for Analysis Services) and delivered to analysis services instance. AMO class library is equipped with various classes with the topmost class being the ‘Server.’' It follows the database, cubes, measure groups, measures, dimensions, perspectives.

The analysis services instance can have multiple databases, with AMO you can connect to one of them or there can be multiple instances of analysis services.

We need to connect to the server which has analysis services installed. Following code will list all existing analysis service databases from a server

//declare object
Server server;
try
{
//connect to analysis server
server = new Server();
//if it is named instance server name should follow instance name
server.Connect(@"data source=<server name>");
//list all the databases from the server in listbox
foreach (Database database in server.Databases)
{
listBox1.Items.Add(database.Name);
//list all the data sources for each database in listbox
foreach (DataSource ds in database.DataSources)
{
listBox2.Items.Add(ds.Name);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

AMO can be useful if we want to show users when were the cube or partitions last processed.

With the following code, we will choose one database from the analysis server and display the cubes, measure groups, measures, partitions and dimensions from it in a Listbox

//declare objects
Server server;
Database db=new Database();

try
{
//connect to analysis server
server = new Server();
server.Connect(@"data source=<server name>");
db = server.Databases["<database name>"];
foreach (Cube cube in db.Cubes)
{
//will process full cube
cube.Process(ProcessType.ProcessFull);
listBox3.Items.Add("Default measure: " + cube.DefaultMeasure);
//list all cube names
listBox3.Items.Add("Cube: " + cube.Name);
//last processed is current time as we processed full
listBox3.Items.Add(cube.LastProcessed);
foreach (MeasureGroup measuregrp in cube.MeasureGroups)
{
//list all measure groups
listBox3.Items.Add("Measure Group: " + measuregrp.Name);
foreach (Partition partition in measuregrp.Partitions)
{
//give partition name
listBox3.Items.Add("Partition: " + partition.Name);
//list data and time when partition was last processed
listBox3.Items.Add(partition.LastProcessed);
}
foreach (Measure measure in measuregrp.Measures)
{
//list measure for current measure group
listBox3.Items.Add("Measure: " + measure.Name);
}
}
}
foreach (Dimension dim in db.Dimensions)
{
//list all dimensions
listBox3.Items.Add("Dim: " + dim.Name);
}
}
//capture specific exception
catch (AmoException amoEx)
{
MessageBox.Show(amoEx.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

This post showed how to connect to the analysis server and list all the databases. We then chose a database and displayed the cubes, measures, partitions and dimensions in a Listbox.

In the next part of this article, I will demonstrate the foolowing

- how to take backups of MultiDimensional Anaysis Service Databases,

- explore the security part of how to add a role, a member to role and how to provide the necessary permissions.

- how to create new databases, data sources, data source views and required tables .

- how AMO offers to wok without connecting to the analysis services instance.