Kategoriarkiv: English posts

Qlikview set analysis vs MDX

I come from a Microsoft background but have made a couple of forays into the world of Qlikview. I noticed that just like MDX is used to enhance reports and cubes when working in the Microsoft BI environment, Qlikview provides set analysis expressions as a tool for sorting and querying subsets of data within a Qlikview document. The set analysis syntax is quite different from MDX so I thought a little comparison using the AdventureWorks data structure might be worthwhile.

In MDX a query scoped member or calculated measure totaling the sales amount for male customers would look like this:

sum([DimCustomer].[Gender].&[M],[Measures].[Sales Amount])

Translated into a Qlikview set analysis expression we end up with the following:

=sum({$<Customer.Gender = {'M'}>} OrderDetails.[Sales Amount])

An MDX scoped member or calculated measure totaling the sales for all male customers in Germany and France would be defined using a crossjoin (note the * operator):

sum({[DimSalesTerritory].[SalesTerritoryCountry].&[France],
[DimSalesTerritory].[SalesTerritoryCountry].&[Germany]} *
[DimCustomer].[Gender].&[M], [Measures].[Sales Amount])

Using Qlikview set analysis the above would look like this:

=sum({$ < Customer.Gender = {'M'},
SalesTerritory.SalesTerritoryCountry =
{'Germany','France'}>} OrderDetails.SalesAmount)

A total excluding France and Germany would look like this in MDX:

sum(except([DimSalesTerritory].[SalesTerritoryCountry].children,
{[DimSalesTerritory].[SalesTerritoryCountry].&[France],
[DimSalesTerritory].[SalesTerritoryCountry].&[Germany]},
[Measures].[Sales Amount])

using Qlikview set analysis we get this:

=sum({$ <SalesTerritory.SalesTerritoryCountry =
-{'Germany','France'} >} } OrderDetails.SalesAmount)

An mdx query calculating the two years with the highest sales from the AdventureWorks cube would look like this:

SELECT
sum(TOPCOUNT([DimDate].[CalendarYear].[CalendarYear], 2,
[Measures].[Sales Amount]),[Measures].[Sales Amount])
ON 0
from [AWModel]

In Qlikview you would create a chart displaying Sales Amount by Calendar Year and then select Properties–>Dimension Limits and check the ”Select which values are displayed” check box so you can set the chart to display only the two largest values.

Or you can create a list box for Calendar Year next to your chart and type the following set analysis expression into the search box:

=rank(sum(OrderDetails.SalesAmount))<=2

and press enter. The two years with the largest sales are now selected by the expression you just created.

I think it is quite safe to assume that one of the reasons it is quite time consuming to replace an OLAP-cube with a Qlikview document or vice versa, are the syntactical differences.

Notes: I have used the sum function on the MDX-statements for the sake of clarity. It is actually not needed since the property of the measure itself is set to sum in the SSAS cube definition. If we substitute the ”$” for ’1′, our selections will be independent of any selections in the Qlikview document.


Using MDX for rolling totals relative to current date.

If there is one thing I have learned during my career, it’s that managers and accountants love rolling totals and averages.

The needs might differ slightly depending on the type of business, an accountant or a financial controller is probably interested in a rolling total showing closed results only which in many cases means comparing previous month and months preceding it. A lot of retail operations close sales books every day, and someone monitoring sales is therefore interested in comparing yesterday’s results with the days preceding.

To retrieve data sets containing data for previous periods relative to today, we need to obtain the current date and this is surprisingly complicated if you are using MDX. One way to do it is by invoking VBA functions and parsing strings, but a better way to retrieve the current date is to simply create a named set that stores current date as suggested by Mosha Pashumansky.  We can then create a C# executable or SSIS script task that updates the named set to today’s date. To create a simple executable using AMO:

1. Open Visual Studio 2010 and create a console project.

2. Add references to System.Configuration and Microsoft.AnalysisServices (found in C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies for MS SQL 2008 R2) to your project.

3. Add the following namespaces to your Program.cs file:

using System.Globalization;

using Microsoft.AnalysisServices;

using System.Globalization;

4. Open the cube in  your SSAS solution, click the calculations tab, then click the  Script View tab and copy the entire MDX script.

5. Add a text file to  your console project and paste the MDX script into this file.

6.  Use the following code snippet to create the named set:

string connectionString = ConfigurationManager.ConnectionStrings["adw"].ConnectionString;

Server asServer = new Server();

asServer.Connect(connectionString);

Database adw = asServer.Databases.GetByName(ConfigurationManager.AppSettings["db"]); // this throws exception if the database is not found

Cube cb = adw.Cubes[0];

//CultureInfo helps us override local date format settings:

CultureInfo ci = new CultureInfo(”en-US”, false);

System.Threading. Thread.CurrentThread.CurrentCulture = ci;

DateTime time = DateTime.Now; // Use current time
string format = ”MMMM d, yyyy”;

Console.WriteLine(time.ToString(format));

//Retrieve cube mdx script:
MdxScript first = cb.MdxScripts[0];

//Read script minus [Today]
string text = System.IO. File.ReadAllText(@”C:\MDXScript.txt”);

//text = text + ”Create Set CurrentCube.[Today] As {[Date].[Date].[June 1, 2002]};”;
//Append [Today]:
text = text + ”Create Set CurrentCube.[Today] As {[Date].[Date].[" + time.ToString(format) + "]}”;

//Replace cube mdx script with the parsed script.
first.Commands[0].Text = text;

first.Update();

cb.Update();
adw.Update();

cb.Process();

Console.WriteLine(”Processing started”);

adw.Process();

asServer.Dispose();

Console.WriteLine(”processing finished”);

Console.ReadLine();

The above code could also be run inside an SSIS script component.The problem with Adventure Works is that the time dimension data is a couple of years old, so for demo purposes you want to use a hard coded date instead of the current date.

We can now use the [Today] named set to retrieve rolling averages and totals. The query below let’s us look at the reseller sales” for the last 100 days ending with yesterday. Note the use of the Lag function which retrieves yesterday’s date:

select [Measures].[Reseller Sales Amount] on 0,
LastPeriods(100,[Today].Item(0).Lag(1)) on 1
from [Adventure Works]

The current date named set can also be used together with the  ParallellPeriod function, but that’s another post.


Calculated measures and query scoped members in SSRS

Calculated Measures are a powerful feature in OLAP cubes. They are used to extend cube functionality trough combining actual measures and dimensions with each other. A calculated measure is defined in the cube whereas a query scoped member is defined in an MDX-query.  Sometimes a query scoped member might be the only option due to user rights or performance reasons.

Let’s say we want to define a query-scoped member that allows us to subtract the previous period’s sales from the current period to find out whether sales increased or decreased. First open SQL Server Management Studio then connect to  SSAS,  open the Adventure Works database and create the following query:

with member [Measures].[Sales Growth] as
([Measures].[Reseller Sales Amount]) -
(ParallelPeriod([Date].[Calendar].[Month],1,[Date].[Calendar].CurrentMember),[Measures].[Reseller Sales Amount])
select {[Measures].[Reseller Sales Amount],[Measures].[Sales Growth]} on columns,
non empty [Date].[Calendar].[Month].Members on rows
from [Adventure Works]

To define Sales Growth we make use of the ParallelPeriod function and the CurrentMember property. Let’s dissect the ParallelPeriod function call:

The first parameter is the Month level of the [Date].[Calendar] hierarchy. It defines the period:

(ParallelPeriod([Date].[Calendar].[Month],1,[Date].[Calendar].CurrentMember),[Measures].[Reseller Sales Amount])

The second parameter defines the period lag. By passing 1 as an argument we get the previous month:

(ParallelPeriod([Date].[Calendar].[Month],1,[Date].[Calendar].CurrentMember),[Measures].[Reseller Sales Amount])

The third argument defines the hierarchy which we will use to slice the value. The CurrentMember property tells the execution engine to look for the current member of the other axis:

(ParallelPeriod([Date].[Calendar].[Month],1,[Date].[Calendar].CurrentMember),[Measures].[Reseller Sales Amount])

Note that we can use both the [Date]. [Calendar].[Month] and [Date]. [Calendar].[Date] levels of the [Date].[Calendar] hierarchy to slice the data along the rows-axis. I.e.: [Date].[Calendar].[Date].Members  or [Date].[Calendar].[Month].Members

The above was all done in SQL Server Management Studio, to accomplish the same in an SSRS report:

  1. Fire up BIDS, and start a Report Wizard project that we connect to the SSAS Adventure Works database. Click next.
  2. Open the query designer, drag the [Date].[Calendar].[Month] dimension and [Measures].[Reseller Sales Amount]) to the results pane (the big one).
  3. In the lower left pane of the query designer, right click and select “Add New Calculated Member”.
  4. In the Calculated Member Builder window paste the following part of the query we designed in Management Studio:([Measures].[Reseller Sales Amount]) -
    (ParallelPeriod([Date].[Calendar].[Month],1,[Date].[Calendar].CurrentMember),[Measures].[Reseller Sales Amount])
  5. Type [Sales Growth] in the uppermost textbox.
  6. Click ok and then simply drag the calculated measure from the lower left pane to the center pane. You should now see both  [Measures].[Reseller Sales Amount] and[Measures].[Sales Growth] data.

You can now go on and create an SSRS report using the query scoped member.


Följ

Få meddelanden om nya inlägg via e-post.