MDX sum over various sets - sum

I have a datacube with a dimension of a large number of academic courses. I would like to group the courses (create a set) by the name of the course (using filter and Inst), so that I can calculate some aggregate statistics. i.e. I would ultimately like a list of the number of people doing courses with those groupings (split later by their home geography).
I have four measures, which include
[Measures].[Achievements]
[Measures].[Starts]
[Measures].[Enrolments]
[Measures].[Leavers]
I have managed to make two sets:
CREATE SET [MyDataCube].[BA] AS
{FILTER(
[Aim].[Aim Title].[Aim Title].Members,
(InStr(1, [Aim].[Aim Title].CurrentMember.NAME, "BA ") <> 0)
)
}
GO
CREATE SET [MyDataCube].[BSc] AS
{FILTER(
[Aim].[Aim Title].[Aim Title].Members,
(InStr(1, [Aim].[Aim Title].CurrentMember.NAME, "BSc") <> 0)
)
}
And then I can query a single set:
SELECT
NON EMPTY [AccessCourses] DIMENSION PROPERTIES MEMBER_NAME ON ROWS,
NON EMPTY Hierarchize({DrilldownLevel({[Geography - Learner Home].[Learner Home].[All]})}) DIMENSION PROPERTIES MEMBER_NAME ON COLUMNS
FROM [MyDataCube]
But how can I get a table with [Geography - Learner Home].[Learner Home].[All] on the rows, with a sum of all [Bsc] and [BA] courses on the columns, like:
I would like to do this for [Measures].[Starts].

Something like the following maybe:
WITH
MEMBER [Aim].[Aim Title].[All].[BA] AS
AGGREGATE(
[BA]
)
MEMBER [Aim].[Aim Title].[All].[BSc] AS
AGGREGATE(
[BSc]
)
SELECT
NON EMPTY
[Geography - Learner Home].[Learner Home].MEMBERS ON ROWS,
NON EMPTY
{
[Aim].[Aim Title].[All].[BA]
,[Aim].[Aim Title].[All].[BSc]
} ON COLUMNS
FROM [MyDataCube]
WHERE [Measures].[Starts];

Related

How can i use a subquery or (WITH)statment in MDX Query?

I have a CustomerToFactor as a Measure and Customer as a Dimension. Now I want to create a MDX code like this SQL code but I can't. because (WITH) statements has another meaning in MDX.
with Total_Customer(
select cus_id
,sum(ctf_price) cus_total_price
from dbo.Customer
join dbo.CustomertoFactor on cus_id = ctf_cus_id
group by cus_id
)
select cus_id
,cus_name
,ctf_date
,ctf_price
,(cus_total_price / 100 * ctf_price) as Price_pro_customer
from dbo.Customer
join dbo.CustomertoFactor on cus_id = ctf_cus_id
join Total_Customer on Total_customer.cus_id = dbo.Customer.cus_id
SELECT NON EMPTY { [Measures].[ctf_date]
,[Measures].[ctf_price]
, (?) Price_pro_customer
} ON COLUMNS
,NON EMPTY {[Customer].[Customer - cus_name].[Customer - cus_name].ALLMEMBERS}
FROM [CustomerToFactor]
Thanks for your Answers. but it doesn't work. Actually I want it to be grouped for every name you name. for Example: for the name Alex only the sum would have to be calculated for Alex(100+300 = 400) as well as Group by.
I do not really understand the point of the calculation :)
But anyway, in MDX you can have your own measures calculated like this:
WITH MEMBER [Measures].[Price_pro_customer] AS
(SUM([Measures].[ctf_price]) / 100 * [Measures].[ctf_price])
SELECT NON EMPTY { [Measures].[ctf_date]
,[Measures].[ctf_price]
,[Measures].[Price_pro_customer]
} ON COLUMNS
,NON EMPTY {[Customer].[Customer - cus_name].[Customer - cus_name].ALLMEMBERS}
FROM [CustomerToFactor]
I am not sure you'll get the same result as the SQL query though, since you have [Customer].[Customer - cus_name].[Customer - cus_name].ALLMEMBERS on the rows which basically does a GROUP BY on the customer name.
So if in the table you had several rows for the same customer the output of MDX query should be 1 row for each customer. The SUM([Measures].[ctf_price]) is also different since it sums over all customers
I think you should create a date dimension reference to ctf_date.
Then your mdx should be as below:
WITH MEMBER [Measures].[Price_pro_customer] AS
SUM([DimDate].[ctf_date].[All], [Measures].[ctf_price]) / 100 * [Measures].[ctf_price]
SELECT NON EMPTY {
[Measures].[ctf_price] ,
[Measures].[Price_pro_customer]
} ON COLUMNS ,
NON EMPTY {[Customer].[Customer - cus_name].[Customer - cus_name].ALLMEMBERS *
[DimDate].[ctf_date].[ctf_date].ALLMEMBERS} ON ROWS
FROM [CustomerToFactor]

MDX,how to flatten the result of Descendants function

I have a hierarchy with 5 level,I use Descendants() to retrieve all lower level of a member.But i end up with a one column result where i like to have a result with one column for each level.So on each row i repeat the parent,grand parents etc of the current member.
WITH
MEMBER [Measures].[key] AS
[DimGLAcct].[MgtCOA].CurrentMember.UNIQUENAME
MEMBER [Measures].[level_] AS
[DimGLAcct].[MgtCOA].CurrentMember.level.ordinal
SELECT
{
[Measures].[key]
, [Measures].[level_]
, [Measures].[Actuals]
} ON COLUMNS,
{
Descendants(
[DimGLAcct].[MgtCOA].[Mparent5].&[MCOA].&[400000M - Total operating overhead expenses].&[440000M - Other expenses].&[441000M - Other expenses]
,
,SELF_AND_AFTER
)
} ON ROWS
FROM [Model];
I cannot quite suss out the names of your levels but it is ok to do the following in mdx:
WITH
MEMBER [Measures].[key] AS
[DimGLAcct].[MgtCOA].CurrentMember.UNIQUENAME
MEMBER [Measures].[level_] AS
[DimGLAcct].[MgtCOA].CurrentMember.level.ordinal
SELECT
{
[Measures].[key]
, [Measures].[level_]
, [Measures].[Actuals]
} ON COLUMNS,
[DimGLAcct].[LevelX]
*[DimGLAcct].[LevelY]
*[DimGLAcct].[LevelZ]
*[DimGLAcct].[LevelK]
ON ROWS
FROM [Model];
Each of the levels in your user hierarchy will have respective attribute hieraries - which are used in the above.

How to mimic SQL subtraction of results from two different queries in mdx

I wanted to do the trend analysis between the dates. For an instance current date- 30 days
30-60 days and so on.Below is the snippet of comparable sql query but same I wanted to do in MDX.
SQL
SELECT
ROUND
(
(
(
(
SELECT
SUM(del_pri_impr)
FROM
reporting.so_sli_calc_val a,
reporting.user_group_tenant b,
reporting.salesorder c
WHERE
created_on BETWEEN DATE(now()-30) AND DATE(now())
)
-
(
SELECT
SUM(del_pri_impr)
FROM
reporting.so_sli_calc_val a,
reporting.user_group_tenant b,
reporting.salesorder c
WHERE
created_on BETWEEN DATE(now()-60) AND DATE(now()-30)
)
)
/
(
SELECT
SUM(del_pri_impr)
FROM
reporting.so_sli_calc_val a,
reporting.user_group_tenant b,
reporting.salesorder c
WHERE
created_on BETWEEN DATE(now()-60) AND DATE(now()-30)
) *100
)
,
0
) AS trend
MDX:
WITH
SET [~FILTER] AS
{[Created_Date.Created_Hir].[Created_On].[2014-04-01]:[Created_Date.Created_Hir].[Created_On].[2014-04-30]}
SET [~ROWS] AS
{[Sales Order Attributes SO.Sales_order].[Sales Order ID].Members}
SELECT
NON EMPTY {[Measures].[CONT_AMT_GROSS], [Measures].[CONT_AMT_NET]} ON COLUMNS,
NON EMPTY [~ROWS] ON ROWS
FROM [SALES_ORDER]
WHERE [~FILTER]
As of now I have hard coded the dates, that will come from parameters.
I am facing difficulty in creating the second set and how to do subtraction between two sets in MDX.
You already have the logic on how to obtain sets of date corresponding to "last 30 days from now" and "last 60 to last 30 days from now". So, I am going to skip that part.
NOTE - You would have to use the parameter values while building these sets.
What you want to do here is first find the values corresponding to these sets of dates and then perform operations on them.
You can proceed like this -
WITH
SET [~FILTER] AS
{[Created_Date.Created_Hir].[Created_On].[2014-04-01]:[Created_Date.Created_Hir].[Created_On].[2014-04-30]}
SET [~ROWS] AS
{[Sales Order Attributes SO.Sales_order].[Sales Order ID].Members}
SET [Last30Days] AS
...
SET [Last60ToLast30Days] AS
...
MEMBER [~Last30Days - Now] AS
Aggregate
(
[Last30Days],
[Measures].[SomeMeasure]
)
MEMBER [~Last60Days - Last30Days] AS
Aggregate
(
[Last60ToLast30Days],
[Measures].[SomeMeasure]
)
MEMBER [~Measure] AS
([~Last30Days - Now]-[~Last60Days - Last30Days] )/([~Last60Days - Last30Days] * 100), format_string = '#,##0'
SELECT
NON EMPTY {
[Measures].[CONT_AMT_GROSS],
[Measures].[CONT_AMT_NET],
[~Measure]
} ON COLUMNS,
NON EMPTY [~ROWS] ON ROWS
FROM [SALES_ORDER]
Format_String takes care of rounding.
Not sure if I totally agree with Sourav's answer as I think some form of aggregation will be needed; creating tuples with sets in them may raise an exception.
Here is a simple model, against AdvWrks, that is tested and will do a subtraction for you:
WITH
SET [Set1] AS
[Date].[Calendar].[Date].&[20060301]
:
[Date].[Calendar].[Date].&[20070308]
SET [Set2] AS
[Date].[Calendar].[Date].&[20070308]
:
[Date].[Calendar].[Date].&[20080315]
MEMBER [Date].[Calendar].[All].[Set1Agg] AS
aggregate([Set1])
MEMBER [Date].[Calendar].[All].[Set2Agg] AS
aggregate([Set2])
MEMBER [Date].[Calendar].[All].[x] AS
(
[Date].[Calendar].[All].[Set1Agg]
,[Measures].[Internet Sales Amount]
)
MEMBER [Date].[Calendar].[All].[y] AS
(
[Date].[Calendar].[All].[Set2Agg]
,[Measures].[Internet Sales Amount]
)
MEMBER [Date].[Calendar].[All].[x-y] AS
[Date].[Calendar].[All].[x] - [Date].[Calendar].[All].[y]
SELECT
{
[Date].[Calendar].[All].[x]
,[Date].[Calendar].[All].[y]
,[Date].[Calendar].[All].[x-y]
} ON 0
,[Product].[Category].[Category] ON 1
FROM [Adventure Works];
Reflecting against your code maybe something like the following:
WITH
SET [Set1] AS
[Created_Date.Created_Hir].[Created_On].[2014-04-01]
:
[Created_Date.Created_Hir].[Created_On].[2014-04-30]
SET [Set2] AS
[Created_Date.Created_Hir].[Created_On].[2014-03-01]
:
[Created_Date.Created_Hir].[Created_On].[2014-03-31]
MEMBER [Created_Date.Created_Hir].[All].[Set1Agg] AS
Aggregate([Set1])
MEMBER [Created_Date.Created_Hir].[All].[Set2Agg] AS
Aggregate([Set2])
MEMBER [Measures].[~Last30Days - Now] AS
(
[Created_Date.Created_Hir].[All].[Set1Agg]
,[Measures].[SomeMeasure]
)
MEMBER [Measures].[~Last60Days - Last30Days] AS
(
[Created_Date.Created_Hir].[All].[Set2Agg]
,[Measures].[SomeMeasure]
)
MEMBER [Measures].[~Measure] AS
([Measures].[~Last30Days - Now] - [Measures].[~Last60Days - Last30Days])
/
[Measures].[~Last60Days - Last30Days]
* 100
,format_string = '#,##0'
SET [~ROWS] AS
{
[Sales Order Attributes SO.Sales_order].[Sales Order ID].MEMBERS
}
SELECT
NON EMPTY
{
[Measures].[CONT_AMT_GROSS]
,[Measures].[CONT_AMT_NET]
,[Measures].[~Measure]
} ON COLUMNS
,NON EMPTY
[~ROWS] ON ROWS
FROM [SALES_ORDER]
WHERE
[~FILTER];

MDX query - best salesmen who sold all of given products

Let's say I have two simple dimensions:
Products - with id and name
Salesmen - with id and name
My fact table is named SALES and contains the ids of the abovementioned.
I need to produce a query that will show the names of salesmen who sold all of the given products.
This code solves the problem for two items X and Y:
SELECT
{} on 0,
EXISTS(
EXISTS(
{[Salesmen].[Name].MEMBERS},
{[Products].[Name].&[X]}
)
,{[Products].[Name].&[Y]}
)
ON 1
FROM [Test];
The other version is:
SELECT
{} on 0,
INTERSECT(
NONEMPTY(
{[Salesmen].[Name].MEMBERS}
,([Products].[Name].&[X])
)
,NONEMPTY(
{[Salesmen].[Name].MEMBERS}
,([Products].[Name].&[Y])
)
)
ON 1
FROM [Test];
However, this method becomes troublesome if the list of given products is large, for example - 100 random products..
Do you have a property member_key for the hierarchy [Products].[Name] ? We can test like this:
WITH
MEMBER [Measures].[Meas1] AS
[Products].[Name].CurrentMember.PROPERTIES("KEY ID")
MEMBER [Measures].[Meas2] AS
[Products].[Name].CurrentMember.MEMBER_Key
MEMBER [Measures].[Meas3] AS
[Products].[Name].CurrentMember.MEMBERvalue
select
{
[Measures].[Meas1]
,[Measures].[Meas2]
,[Measures].[Meas3]
} on COLUMNS,
[Products].[Name].MEMBERS on ROWS
FROM [Test];
Hopefully one of the custom measures gives you a value? I'll assume Meas2 is working (swap to a different one if Meas1 or Meas3 is returning numbers)
WITH
MEMBER [Measures].[Meas2] AS
[Products].[Name].CurrentMember.MEMBER_Key
SET [ProdsetA] AS
FILTER(
[Products].[Name].MEMBERS
,[Measures].[Meas2] <100
)
SET [ProdsetB] AS
FILTER(
[Products].[Name].MEMBERS
,[Measures].[Meas2] >500
)
SELECT
{} on 0,
INTERSECT(
NONEMPTY(
{[Salesmen].[Name].MEMBERS}
,[ProdsetA]
)
,NONEMPTY(
{[Salesmen].[Name].MEMBERS}
,[ProdsetB]
)
)
ON 1
FROM [Test];
... the >100 and <500 are important. These are the criteria for the filter function to use. The custom set [ProdsetA] will only contain Products that have MEMBER_Key that are <100 whereas the custom set [ProdsetB] will only contain Products that have MEMBER_Key that are >500. You need to use the member values presented to you by the first script to decide what values 100 and 500 should be in your cube context (...I don't know the key values in your cube so just used 100 and 500 as placeholders)

Excluding (All) from sets

I'm using the following but i think there's probably a much simpler method of excluding the All members from the results?
WITH
SET [Non_All_Distributors] AS
{FILTER(
[Distributor Name].members,
(InStr(1, [Distributor Name].CurrentMember.NAME, "All") = 0)
)}
SET [Non_All_Countries] AS
{FILTER(
[Geography Country].members,
(InStr(1, [Geography Country].CurrentMember.NAME, "All") = 0)
)}
SELECT
NON EMPTY
[Dimension].[Hierarchy].DEFAULTMEMBER
ON COLUMNS,
NON EMPTY
[Non_All_Distributors]
*
[Non_All_Countries]
*
Tail([Date].[Date - Calendar Month].[Calendar Day].Members,60)
*
{
[Measures].[Revenue],
[Measures].[NumClients]
}
ON ROWS
FROM [OURCUBE]
Just use
SELECT
NON EMPTY
[Dimension].[Hierarchy].DEFAULTMEMBER
ON COLUMNS,
NON EMPTY
[dimension of Distributor Name].[Distributor Name].[Distributor Name].Members
*
[dimension of Geography Country].[Geography Country].[Geography Country].Members
*
Tail([Date].[Date - Calendar Month].[Calendar Day].Members,60)
*
{
[Measures].[Revenue],
[Measures].[NumClients]
}
ON ROWS
FROM [OURCUBE]
There is no need to define sets here. you can directly state the distributor and country members in the rows clause.
By repeating the attribute name, you restrict the attribute hierarchy - which you refer to by [dim].[attrib name] to the level below the All member, which happens to have the same name as the attribute again. An attribute hierarchy has two levels: level 0 contains the 'All' member and level 1 all the members of the attribute. (This is true only if you did not do special configurations like setting the attribute as non aggregateabable, but I assume the standard case, as you have All members in your hierarchies.
Apart from being more simple, this statement will run much faster, as Filter is a real performance killer in many cases.
I would use the Descendants function and the AFTER option as following; this way you get all the members of the hierarchy below the all member:
select
[Measures].[Amount] on 0,
Descendants([Customers].[Geography].[All], 1, AFTER ) on 1
from [Sales]
(edited: with a request working with MSAS Adv. Works : removed the distance param)
select
Measures].[Order Count] on 0,
Descendants( [Geography].[Geography].[All], , AFTER ) on 1
from [Adventure Works]