MDX bucketizing the result set and assign frequency - ssas

I have a scenario where a result set has to be bucketed and assign a frequency.
For example, the following MDX query:
WITH MEMBER [MEASURES].[PERC_1] AS
AGGREGATE ( EXISTING [DIM CUSTOMER].[CUSTOMER ID].[ALL].CHILDREN,[MEASURES].[AMOUNT])
SELECT
[MEASURES].[PERC_1] ON 0,
[DIM CUSTOMER].[CUSTOMER ID].CHILDREN,[DIM CUSTOMER].[NAME].CHILDREN
FROM [ANALYSIS DW]
WHERE ([DIM CUSTOMER].[ADDRESS].[ALL])
should return this result:
Perc1
C1 10
C2 0
C3 20
C4 30
C5 40
C6 50
C7 50
C8 50
C9 90
C10 100
Now, I want this result set to be divided into buckets. If my bucket size is 3, the buckets should be
0-30
31-60
61-100
These buckets are calculated based on the maximum and minimum values of the perc_1 measure above; i.e., 0 is minimum and 100 is maximum. Buckets are calculated as (0+100)/3 -- (0-30, 31-60, 61-100).
Now the results after the frequency distribution on the above result set should look as below -
frequency
0-30 4
31-60 4
61-100 2
I will not get the access to the OLTP design/SSAS Cube solution.

WITH SET [0-30 set] AS
Filter([DIM CUSTOMER].[CUSTOMER ID].CHILDREN,
[MEASURES].[PERC_1] >= 0 AND [MEASURES].[PERC_1] <= 30
)
SET [31-60 set] AS
Filter([DIM CUSTOMER].[CUSTOMER ID].CHILDREN,
[MEASURES].[PERC_1] >= 31 AND [MEASURES].[PERC_1] <= 60
)
SET [61-100 set] AS
Filter([DIM CUSTOMER].[CUSTOMER ID].CHILDREN,
[MEASURES].[PERC_1] >= 61 AND [MEASURES].[PERC_1] <= 100
)
MEMBER [DIM CUSTOMER].[CUSTOMER ID].[0-30] AS
NULL
MEMBER [DIM CUSTOMER].[CUSTOMER ID].[31-60] AS
NULL
MEMBER [DIM CUSTOMER].[CUSTOMER ID].[61-100] AS
NULL
MEMBER [Measures].[frequency] AS
CASE [DIM CUSTOMER].[CUSTOMER ID].CurrentMember
WHEN [DIM CUSTOMER].[CUSTOMER ID].[0-30] THEN
[0-30 set].Count
WHEN [DIM CUSTOMER].[CUSTOMER ID].[31-60] THEN
[31-60 set].Count
WHEN [DIM CUSTOMER].[CUSTOMER ID].[61-100] THEN
[61-100 set].Count
END
SELECT { [Measures].[frequency] } ON 0,
{
[DIM CUSTOMER].[CUSTOMER ID].[0-30],
[DIM CUSTOMER].[CUSTOMER ID].[31-60],
[DIM CUSTOMER].[CUSTOMER ID].[61-100]
} ON 1
FROM [ANALYSIS DW]
WHERE ([DIM CUSTOMER].[ADDRESS].[ALL])
I do not think you can do this completely within MDX in a way that the "3" is a parameter to the MDX query, as you need at least to have something like the member definitions for the buckets explicitly in the MDX statement. The WITH clause (or a session level CREATE statement) is the only place where you can create the members shown on the row axis, even if they are just used as placeholders and do not contain any logic.
You would need an external tool to generate a parameter specific MDX statement like that above.

Related

how to use MDX average function?

I am VERY new to mdx, and about to learn it. i got into trouble right now ,because i want to get average amount for each weekday between two dates.
For example between 1st of november and 1st of december there are 4 mondays, so average would be [Measures].[Amount] / 4, but i really cant figure out how to express this simple function in MDX
with member [Measures].[Avg] as
avg([Dim Date].[Day Of Week].[Day Of Week]
, [Measures].[amount])
SELECT NON EMPTY { ([Measures].[Avg]), ([Measures].[Amount])} ON COLUMNS,
NON EMPTY { ([Dim Date].[Day Of Week].[Day Of Week].ALLMEMBERS * [Dim Date].[Date Int].[Date Int].ALLMEMBERS )}
ON ROWS FROM ( SELECT ( { [Dim Client].[Common Client UID].&[{xx}] } )
ON COLUMNS FROM ( SELECT ( [Dim Date].[Date Int].&[20151115] : [Dim Date].[Date Int].&[20151215] )
ON COLUMNS FROM [ff]))
WHERE ( [Dim Client].[Common Client UID].&[{xx}] )
This query give me each weekday and all the dates which are bound to the specific weekday, and for each date there are an total amount.
like you can see my average is the same as total. I thought that i could just select all the weekdays between two date, find out for example how many mondays there are and divide it number with amount. but i couldnt figure that out either.
with member [Measures].[avg] as
avg([Dim Date].[Day Of Week].[Day Of Week], [Measures].[Amount])
SELECT NON EMPTY {[Measures].[avg], [Measures].[Amount]} ON COLUMNS, NON EMPTY { ([Dim Date].[Day Of Week].[Day Of Week].ALLMEMBERS )}
ON ROWS FROM ( SELECT ( { [Dim Client].[Common Client UID].&[{xx}] } )
ON COLUMNS FROM ( SELECT ( [Dim Date].[Date Int].&[20151115] : [Dim Date].[Date Int].&[20151215] )
ON COLUMNS FROM [ff]))
WHERE ( [Dim Client].[Common Client UID].&[{xx}] )
result :
How do i solve this?
What you really want is average amount over the dates corresponding to that weekday for the given date range.
You can just move the date range to the definition of calculated member, so that when value for a particular weekday is evaluated, the date range is also considered.
with member [Measures].[Avg] as
avg
(
[Dim Date].[Day Of Week].CURRENTMEMBER * {[Dim Date].[Date Int].&[20151115] : [Dim Date].[Date Int].&[20151215]}
,[Measures].[amount]
)
SELECT NON EMPTY { ([Measures].[Avg]), ([Measures].[Amount])} ON COLUMNS,
NON EMPTY [Dim Date].[Day Of Week].[Day Of Week].ALLMEMBERS * [Dim Date].[Date Int].[Date Int].ALLMEMBERS ON ROWS
FROM [ff]
WHERE ( [Dim Client].[Common Client UID].&[{xx}] )

How to use avg function in mdx?

I have my mdx query:
SELECT
NON EMPTY
{[Measures].[Amount]} ON COLUMNS
,NON EMPTY
{[Dim Date].[Day Of Week].[Day Of Week].MEMBERS} ON ROWS
FROM
(
SELECT
[Dim Date].[Date Int].&[20140730] : [Dim Date].[Date Int].&[20150730] ON COLUMNS
FROM [Cube]
WHERE
[Dim Client].[Common Client UID].&[{some id}]
);
so i have my a weekday dim, which contain members as numbers from 1-7. Query find returns amount for each weekday, which is summed up, but i want to find out an average, so somehow i need to find out how many items was summed to give me [Measures].[Amount] result. I have tryed with separate member function which didnt worked.
WITH MEMBER [Measures].[Avg] AS
Avg(
( [Dim Date].[Day Of Week].CURRENTMEMBER, [Measures].[Amount] )
)
Avg return exectly the same value. How do i do such a request in mdx?
This will give you an average over 1 member:
WITH MEMBER [Measures].[Avg] AS
Avg(
( [Dim Date].[Day Of Week].CURRENTMEMBER, [Measures].[Amount] )
)
That is because CURRENTMEMBER is returning 1 member.
If you want an average over several members than you need to supply a set as the first argument for the Avg function. Here is an example:
WITH MEMBER [Measures].[Daily Avg] AS
Avg(
Descedants(
[Date].[Date - Calendar Month].CURRENTMEMBER
,[Date].[Date - Calendar Month].[Calendar Day]
,[Measures].[Amount]
)
Although I suspect something like the follwoing should work in your context:
WITH
MEMBER [Measures].[Avg] AS
Avg
(
(EXISTING
[Dim Date].[Date Int].MEMBERS)
,[Measures].[Amount]
)
SELECT
NON EMPTY
{
[Measures].[Amount]
,[Measures].[Avg]
} ON COLUMNS
,NON EMPTY
{[Dim Date].[Day Of Week].[Day Of Week].MEMBERS} ON ROWS
FROM
(
SELECT
[Dim Date].[Date Int].&[20140730] : [Dim Date].[Date Int].&[20150730] ON COLUMNS
FROM [Cube]
WHERE
[Dim Client].[Common Client UID].&[{some id}]
);

mdx topcount results not as expected

When I run the query below I only get 2 records back. If I drop the TOPCOUNT, I get 508 records.
Why is it not giving me the top 5 from the 508 records? What am I missing?
with
MEMBER Measures.[EmailCount] as IIF(ISEMPTY([Measures].[Tran Count]), 0 ,[Measures].[Tran Count])
MEMBER Measures.[IncomePerEmail] as
[Measures].[Amount]/ IIF(Measures.[EmailCount] = 0, 1 , Measures.[EmailCount] )
MEMBER Measures.[Income Range] as
CASE
WHEN Sum(EXISTING [Dim IFA Details].[Parent Key].[Adviser Group].Members,
Measures.[Amount] ) <= 10000 THEN '0-10000'
WHEN Sum(EXISTING [Dim IFA Details].[Parent Key].[Adviser Group].Members,
Measures.[Amount] ) <= 50000 THEN '10001-50000'
WHEN Sum(EXISTING [Dim IFA Details].[Parent Key].[Adviser Group].Members,
Measures.[Amount] ) <= 100000 THEN '50001-100000'
WHEN Sum(EXISTING [Dim IFA Details].[Parent Key].[Adviser Group].Members,
Measures.[Amount] ) <= 200000 THEN '100001-200000'
else '> 200000'
end
SELECT { [Measures].[Amount] , Measures.[EmailCount], measures.[Income Range], Measures.[IncomePerEmail] }
ON COLUMNS,
TOPCOUNT(
NONEMPTY([Dim IFA Details].[Parent Key].[Adviser Group].Members, Measures.Amount)
, 5
, Measures.[IncomePerEmail]
)
having Measures.[Income Range] = '10001-50000'
on rows
FROM [Income and Emails Cube]
where [Dim Date].[Fiscal Year].&[FY 13/14]
You have a HAVING clause which basically filters the set(of 5 records).
TOPCOUNTgets you 5 members but when the filtering happens, only two of the members meet the criteria Measures.[Income Range] = '10001-50000'
If you want to verify this, ORDER the query based on Measures.[IncomePerEmail] and see if the "top 5 records" have income ranges as '10001-50000'

Optimize a MDX Query

I am quite new to MDX and I need some help with this query.
Generate(
filter(
[Dim Products].[Product].[Product].members
*
[Dim Date].week.week.members,
[Measures].[Price]
),
nonempty(
topcount(
[Dim Price].[Price].[Price]
*
[Dim Products].[Product].currentmember
*
[Dim Date].week.currentmember,
1,
[Measures].[Price Count]
)
)
)
I am using the above Named Set in a dashboard tool (Dundas Dashboard) in order to retrieve the MODE (price value that repeats the most). It does show correct results however it is slow. It takes 2-3 seconds if there is a filter on a single week and takes about 6-7 seconds if there is no filter on week (shows data for all weeks). And this is in SSMS but the client tool takes even longer to get the result set, sometimes timing out.
After some tests it appears that the number of the rows in the fact table does not affect the performance, I've drastically decreased it but still the same. The performance increased, however, once I've decreased the number of members in 2 of the dimension tables - [Dim Price], [Dim Products]. Initially it was taking 20+ seconds to get result set but improved to 6-7 seconds once I've decreased the number of members as follows:
Table | Rows Before | Rows After
[Dim Price] | 2400 | 620
[Dim Products] | 1080 | 101
This makes me think there is a Cartesian Product between the 3 dimensions that is affecting the performance.
I need someone to advise how I can improve the query to increase the performance.
Try the EXISTING function before the NONEMPTY function like this:
Generate(
filter(
[Dim Products].[Product].[Product].members
*
[Dim Date].week.week.members,
[Measures].[Price] //<<<<<<<<<<SHOULD THIS NOT INCLUDE A CONDITION e.g. [Measures].[Price] > 0 ?
),
EXISTING nonempty(
topcount(
[Dim Price].[Price].[Price]
*
[Dim Products].[Product].currentmember
*
[Dim Date].week.currentmember,
1,
[Measures].[Price Count]
)
)
)
Attempt 2:
Generate(
filter(
NONEMPTY(
[Dim Products].[Product].[Product].members
*
[Dim Date].week.week.members,
[Measures].[Price]
)
),
EXISTING nonempty(
topcount(
[Dim Price].[Price].[Price]
*
[Dim Products].[Product].currentmember
*
[Dim Date].week.currentmember,
1,
[Measures].[Price Count]
)
)
)
Attempt 3:
GENERATE(
FILTER(
NONEMPTY(
[Dim Products].[Product].[Product].MEMBERS
*
[Dim Date].week.week.MEMBERS
),
[Measures].[Price] > 0
),
TOPCOUNT(
NONEMPTY(
EXISTING
[Dim Price].[Price].[Price]
*
[Dim Products].[Product].currentmember
*
[Dim Date].week.currentmember,
[Measures].[Price Count]
),
1,
[Measures].[Price Count]
)
)

Filtering hierarchies in MDX WITH clause

By using the answer of MDX on multiple hierarchical dimensions I have the following MDX query:
with
member L1Y1 as ([Dim Location].[Hierarchy].[Center].&[1],
[Dim Date].[Calendar Date].[Year].&[2010],
[Measures].[x])
select ([Dim Attribute].[Parent Code].&[10000]) on 0,
({L1Y1}) on 1
from DS
Now the problem is how to filter the L1Y1 based on its children. For example suppose we want to filter it so that only season 2 and month 7 included in the query. The following query output is the same as above and the where clause has no effect:
with
member L1Y1 as ([Dim Location].[Hierarchy].[Center].&[1],
[Dim Date].[Calendar Date].[Year].&[2010],
[Measures].[x])
select ([Dim Attribute].[Parent Code].&[10000]) on 0,
({L1Y1}) on 1
from DS
where [Dim Date].[Calendar Date].[Season].&[2] and
[Dim Date].[Calendar Date].[Month].&[7]
Unless you removed the attributes you should have multiple hierarchies available in your time dimension. Can you try this :
with
member L1Y1 as ([Dim Location].[Hierarchy].[Center].&[1],
[Dim Date].[Calendar Date].[Year].&[2010],
[Measures].[x])
select ([Dim Attribute].[Parent Code].&[10000]) on 0,
({L1Y1}) on 1
from DS
where [Dim Date].[Season].&[2] and [Dim Date].[Month].&[7]
Note, as we are not using the hierarchy [Calendar Date] time member is not overwritten but filtered.
How about:
with
member L1Y1 as ([Dim Location].[Hierarchy].[Center].&[1],
[Dim Date].[Calendar Date].[Year].&[2010],
[Measures].[x])
member S2 as ([Dim Location].[Hierarchy].[Center].&[1],
[Dim Date].[Calendar Date].[Season].&[2],
[Measures].[x])
member M7 as ([Dim Location].[Hierarchy].[Center].&[1],
[Dim Date].[Calendar Date].[Month].&[7],
[Measures].[x])
select ([Dim Attribute].[Parent Code].&[10000]) on 0,
(L1Y1, S2, M7) on 1
from DS
I understand it is a somewhat more laborous approach, but you would get the result. If you otherwise want to get a single value only you can try keeping only the M7 measure.