MDX Calculated Members by Month - ssms

I'm working on a query in SSMS 2014 where I need [Measures].[Percent Unique App Users] by month, so that I may dynamically calculate the percent increase month over month. I'd preferably like to start with comparing last month to the month prior, and this will eventually end up being run as a report every month (hence the need for it to be dynamic).
With or without my DATE Members, the count of [CurrentMonthUniqueUsers] and [LastMonthUniqueUsers] stay the same (show in example). Additionally, without my WHERE statement, I only get results for my non-MEMBER rows.
WITH
MEMBER [LastMonthUniqueUsers] as SUM({[Date].[Calendar].CurrentMember.Lag(2)},[Measures].[App Unique Users Loggedin])
MEMBER [CurrentMonthUniqueUsers] as SUM({[Date].[Calendar].CurrentMember.Lag(1)},[Measures].[App Unique Users Loggedin])
//
MEMBER [Measures].[Percent Unique App Users] AS ([CurrentMonthUniqueUsers]) / ([LastMonthUniqueUsers]), FORMAT_STRING = "Percent"
//
MEMBER [CurrentDate] As Now()
//
SELECT
non empty[Project].[Client Name].children ON 0,
non empty{[Measures].[Percent Unique App Users],
[CurrentMonthUniqueUsers],
[LastMonthUniqueUsers],
[Measures].[App Logins],
[Measures].[App Unique Users Loggedin]} ON 1
FROM
[OpsViewWarehouse]
WHERE
[Date].[Year].[2017]
The image below is what I am getting for results. The login counts and unique login counts are for all time and are not getting filtered by any date.
Data Screenshot
I'm new to MDX, so any help would be greatly appreciated!
EDIT (5/22/2017): Here is the query that worked with the answer I was provided by whytheq. Note that it isn't yet dynamic in that I have to specify a date (201705) on the column.
WITH
MEMBER [2MonthsPriorUniqueUsers] AS Sum({[Date].[Calendar Year Month].CurrentMember.Lag(2)},[Measures].[App Unique Users Loggedin])
MEMBER [1MonthPriorUniqueUsers] AS Sum({[Date].[Calendar Year Month].CurrentMember.Lag(1)},[Measures].[App Unique Users Loggedin])
MEMBER [1MonthPriorTotalAppLogins] AS Sum({[Date].[Calendar Year Month].CurrentMember.Lag(1)},[Measures].[App Logins])
MEMBER [Measures].[PercentUniqueIncreaseMoM] AS ([1MonthPriorUniqueUsers] / [2MonthsPriorUniqueUsers])-1,FORMAT_STRING = "Percent"
SELECT
NON EMPTY
{[Date].[Calendar Year Month].[201705]}
* [Project].[Client Name].members ON 0,
NON EMPTY
{
[Measures].[PercentUniqueIncreaseMoM]
,[1MonthPriorUniqueUsers]
,[2MonthsPriorUniqueUsers]
,[1MonthPriorTotalAppLogins]
} ON 1
FROM [OpsViewWarehouse]

In your query your CURRENTMEMBER is what is in the WHERE clause - which is 2017 - so this won't be returning a month - it's probably returning the member [Date].[Calendar].[All]:
[Date].[Calendar].CurrentMember
Try adding months into either the SELECT or WHERE clause:
//
WITH
MEMBER [LastMonthUniqueUsers] AS
Sum
(
{[Date].[Calendar].CurrentMember.Lag(2)}
,[Measures].[App Unique Users Loggedin]
)
MEMBER [CurrentMonthUniqueUsers] AS
Sum
(
{[Date].[Calendar].CurrentMember.Lag(1)}
,[Measures].[App Unique Users Loggedin]
)
//
MEMBER [Measures].[Percent Unique App Users] AS
[CurrentMonthUniqueUsers] / [LastMonthUniqueUsers]
,FORMAT_STRING = "Percent"
//
MEMBER [CurrentDate] AS
Now()
SELECT
NON EMPTY
[Project].[Client Name].Children ON 0
,NON EMPTY
{[Date].[Calendar].[Month].&[Jan 2017]} //<<<<<<change to a month member in your cube
* {
[Measures].[Percent Unique App Users]
,[CurrentMonthUniqueUsers]
,[LastMonthUniqueUsers]
,[Measures].[App Logins]
,[Measures].[App Unique Users Loggedin]
} ON 1
FROM [OpsViewWarehouse];
The above could also be a set of months:
//
WITH
MEMBER [LastMonthUniqueUsers] AS
Sum
(
{[Date].[Calendar].CurrentMember.Lag(2)}
,[Measures].[App Unique Users Loggedin]
)
MEMBER [CurrentMonthUniqueUsers] AS
Sum
(
{[Date].[Calendar].CurrentMember.Lag(1)}
,[Measures].[App Unique Users Loggedin]
)
//
MEMBER [Measures].[Percent Unique App Users] AS
[CurrentMonthUniqueUsers] / [LastMonthUniqueUsers]
,FORMAT_STRING = "Percent"
//
MEMBER [CurrentDate] AS
Now()
SELECT
NON EMPTY
{
[Measures].[Percent Unique App Users]
,[CurrentMonthUniqueUsers]
,[LastMonthUniqueUsers]
,[Measures].[App Logins]
,[Measures].[App Unique Users Loggedin]
} ON 0
,NON EMPTY
{Date].[Calendar].[Month].MEMBERS //<<<<<<change to the month hierarchy in your cube
* [Project].[Client Name].Children ON 1
FROM [OpsViewWarehouse];

Related

SSAS MDX sum up on memberships in date hierarchies?

In a cube that contains memberships of a club, I have a column MembersInOut in my fact-table which holds when a member joined the club (Value = 1) and leaving (value = -1). The Club started jan 1. 2000. so no members before that date.
Now to know the current number of members on a specific date I can do this:
CREATE MEMBER CURRENTCUBE.[Measures].[Calculated MembersOfTheClub]
AS
Sum(
{[Date Dim].[Date].&[2000-01-01T00:00:00]:
[Date Dim].[Date].currentmember},
[Measures].[MembersInOut]
)
This works fine on the actuel date, but how to make this work on a date hierarchie [Year-Month-day] ?
Thanks
You could create Y-M-D hierarchy, then use expression like below
with member[Measures].[S1] AS
sum(
{NULL:[Date].[Calendar Date].CurrentMember}
, [Measures].[Internet Sales Count])
select nonempty ([Date].[Calendar Date].members) on rows, nonempty ({[Measures].[S1],[Measures].[Internet Sales Count]}) on columns from [Analysis Services Tutorial]
Zoe

MDX - Running Sum over months limited to an interval

I have a query that after some sweating and some swearing works
WITH
MEMBER [Measures].[m_active] AS ([Measures].[CardCount], [Operation].[Code].[ACTIVATION])
MEMBER [Measures].[m_inactive] AS ([Measures].[CardCount], [Operation].[Code].[DEACTIVATION])
MEMBER [Measures].[p_active] AS
SUM(
[Calendar.YMD].[2016].[January]:[Calendar.YMD].CurrentMember,
[Measures].[m_active]
)
MEMBER [Measures].[p_inactive] AS
SUM(
[Calendar.YMD].[2016].[January]:[Calendar.YMD].CurrentMember,
[Measures].[m_inactive]
)
MEMBER [Measures].[tot_active] AS (
SUM({[Calendar.YMD].[2010].Children}.Item(0):[Calendar.YMD].CurrentMember, [Measures].[m_active]) -
SUM({[Calendar.YMD].[2010].Children}.Item(0):[Calendar.YMD].CurrentMember, [Measures].[m_inactive])
)
MEMBER [Measures].[p_tot_active] AS
SUM(
[Calendar.YMD].[2016].[January]:[Calendar.YMD].CurrentMember,
[Measures].[tot_active]
)
SELECT
{[Measures].[m_active], [Measures].[p_active], [Measures].[m_inactive], [Measures].[p_inactive], [Measures].[tot_active], [Measures].[p_tot_active]} ON COLUMNS,
NonEmptyCrossJoin(
{Descendants([Calendar.YMD].[2016].[January]:[Calendar.YMD].[2017].[August], [Calendar.YMD].[Month])},
{Descendants([CardStatus.Description].[All CardStatus.Descriptions], [CardStatus.Description].[Description])}
) on ROWS
FROM [Cube]
What I obtain is a table that for each months show the activation and deactivation relative to that month, the accumulated activations relative to the period considered (starting from 1 January 2016 and ending 1 August 2017) and the total active cards from the beginning of time (january 2010) until the end time interval.
This interval is parametrized and the day are to be considered, with this query all the activations made in august are considered even the ones made after the 1st.
I try to make some modifications like this.
WITH
MEMBER [Measures].[m_active] AS ([Measures].[CardCount], [Operation].[Code].[ACTIVATION])
MEMBER [Measures].[m_inactive] AS ([Measures].[CardCount], [Operation].[Code].[DEACTIVATION])
MEMBER [Measures].[p_active] AS
SUM(
[Calendar.YMD].[2016].[January].[1]:[Calendar.YMD].CurrentMember,
[Measures].[m_active]
)
MEMBER [Measures].[p_inactive] AS
SUM(
[Calendar.YMD].[2016].[January].[1]:[Calendar.YMD].CurrentMember,
[Measures].[m_inactive]
)
MEMBER [Measures].[tot_active] AS (
SUM({[Calendar.YMD].[2010].[January].Children}.Item(0):[Calendar.YMD].CurrentMember, [Measures].[m_active]) -
SUM({[Calendar.YMD].[2010].[January].Children}.Item(0):[Calendar.YMD].CurrentMember, [Measures].[m_inactive])
)
MEMBER [Measures].[p_tot_active] AS
SUM(
[Calendar.YMD].[2016].[January].[1]:[Calendar.YMD].CurrentMember,
[Measures].[tot_active]
)
SELECT
{[Measures].[m_active], [Measures].[p_active], [Measures].[m_inactive], [Measures].[p_inactive], [Measures].[tot_active], [Measures].[p_tot_active]} ON COLUMNS,
NonEmptyCrossJoin(
{Descendants([Calendar.YMD].[2016].[January]:[Calendar.YMD].[2017].[August], [Calendar.YMD].[Month])},
{Descendants([CardStatus.Description].[All CardStatus.Descriptions], [CardStatus.Description].[Description])}
) on ROWS
FROM [Cube]
But I get this error on the relative fields:
#ERR: mondrian.olap.fun.MondrianEvaluationException: Members must belong to the same level
How can i solve this? Thanks.

How to calculate average on two dimensions in MDX

I'm trying to convert the following SQL query into a calculated member in my SSAS cube.
SELECT ActionKey, AVG(1.0 * Days) AS AverageDays
FROM( SELECT ActionKey, UserKey, COUNT(DISTINCT DateKey) AS Days
FROM [TEST].[dbo].[FactActivity]
GROUP BY ActionKey, UserKey) a
GROUP BY ActionKey
How do I do this in MDX? I tried the following but it's giving me wrong result
IIF([Measures].[Dim User Count] = 0, 0 , [Measures].[Dim Date
Count]/[Measures].[Dim User Count])
In my cube, I have two derived measures . "Dim Date Count" which is count of rows in DimDate table and "Dim User Count" which is count of row of DimUser table. Both have many-many relationship with other dimensions of the cube, so i can calculate the distinct days and users easily.
This worked
AVG([Users].[User Key].[User Key], [Measures].[DATE COUNT])
(not a solution but maybe helps)
Are the two measures that you've created giving the results you expect? If you run the equivalent of the following against [YourCube] is it just the new measure [Measures].[AverageDays] that is wrong?
SELECT
NON EMPTY
{
[Measures].[AverageDays]
,[Measures].[Dim Date Count]
,[Measures].[Dim User Count]
} ON COLUMNS
,NON EMPTY
{
[Action].[Action].MEMBERS
*
[Date].[Calendar].[Month].ALLMEMBERS
} ON ROWS
FROM [YourCube];

Using intersect with 2 large sets to get the distinct count - MDX

I have a calculated member which represents an active customer. That would be the following;
WITH MEMBER [Measures].[Active Customers] AS
Count ( nonempty( Filter (
( [Customer].[Customer Key].Members, [Measures].[Turnover] ),
[Measures].[Turnover] > 0
) ) )
This works great, when I want to get active customers in the current period and previous ones, as I get my time dimension, and use the CurrentMember, CurrentMember.PrevMember and CurrentMember with the Lag function in order to get customers who were active in previous periods.
My problem is when I want to get the count of customers, who are common in different members. Say I want to get customers who are active in the current period, and NOT in the previous period. Or another case, active in current, and active in previous. Because of this, I would need to use the INTERSECT function, and my customer dimension has 4 million records. This is already a subset of 9 million records.
So when checking for a customer who is active in 2 consecutive periods, I do this (The Active Previous Period, and Active Current Period is basically the calculated member above, however with CurrentMember and CurrentMember.PrevMember) :
set [Previous Active Customers Set] AS
Filter (
( [Customer].[Customer Key].Members, [Measures].[Active Previous Period] ),
[Measures].[Active Previous Period] > 0
)
set [Current Active Customers Set] AS
Filter (
( [Customer].[Customer Key].Members, [Measures].[Active Current Period] ),
[Measures].[Active Current Period] > 0
)
member [Measures].[Active 2 consecutive periods] as
count(INTERSECT([Current Active Customers Set],[Previous Active Customers Set]) )
This takes forever. Is there anyway to improve, or go around this performance problem of using the INTERSECT with large sets? Or maybe optimizations on the MDX query? I tried always using a subset of my customers dimension, but this only reduced the number of records to less than 4 million - so it's still large. Any help would be appreciated!
I would assume you can speed this up if you avoid using named sets and calculated members as far as possible.
One step towards this would be as follows: Create a new fact table with foreign keys just to your customer and time dimension, and add a record to it if a customer was active on that day. Build a measure group, let's say "activeCustomers" based on this table, just using "count" as the measure. But make this invisible, as we do not need it.
Then, you can replace
count( nonempty( Filter (
( [Customer].[Customer Key].Members, [Measures].[Turnover] ),
[Measures].[Turnover] > 0
) ) )
with
count( Exists(
[Customer].[Customer Key].Members,
<state your time selection here>,
"activeCustomers"
) )
Exists should be more efficient than Filter.
Another optimization approach could be the observation that instead of intersecting two sets generated via Filter, you could define one set with a more complex filter, avoiding that AS is looping along the customers twice, and then intersecting the results:
set [Active Customers Set] AS
Filter (
( [Customer].[Customer Key].Members, [Measures].[Active Previous Period] ),
[Measures].[Active Previous Period] > 0
AND
[Measures].[Active Current Period] > 0
)

Can I use the Lag function against a custom member

In the following I successfully find the last complete month and the billable income related to that month. Then I'd like to target the month before and find the income for that month. Thought that Lag would be a good function to use but this doesn't seem to work. No errors just no data returned for the second column [Measures].[PrevMth_BillInc]:
WITH
MEMBER [Date].[Date - Calendar Month].[LastMth] AS
IIF(
Day(Now()) = 1,
TAIL([Date].[Date - Calendar Month].[Calendar Month],2).Item(1),
TAIL([Date].[Date - Calendar Month].[Calendar Month],2).Item(0)
)
MEMBER [Measures].[LastMth_BillInc] AS
SUM(
[Date].[Date - Calendar Month].[LastMth],
[Measures].[BillableIncome]
)
MEMBER [Measures].[PrevMth_BillInc] AS
SUM(
[Date].[LastMth].Lag(1),
[Measures].[BillableIncome]
)
SELECT
NON EMPTY
{
[Measures].[LastMth_BillInc],
[Measures].[PrevMth_BillInc] //<<<<<this returns nothing
}
ON COLUMNS,
NON EMPTY
ORDER(
[Customer_Dim].[Customer_Hier].[Customer_Level].Members,
[Date].[Date - Calendar Month].[LastMth],
BDESC
)
ON ROWS
FROM [ourCube]
By defining [LastMth] as a calculated member, you define a new member of the [Date].[Date - Calendar Month], not an alias to an existing one. Hence Lag does not work, as it is applied to the new member - which is somewhere in the hierarchy alongside the original member. And as Lag is a function which operates on the hierarchy structure, it is not to the original member used in its definition.
You could get around that by defining LastMth as a set which would contain the original, non calculated member (to be exact, it would contain a tuple containing the original member) as follows:
WITH
SET [LastTwo] AS
TAIL([Date].[Date - Calendar Month].[Calendar Month],2)
SET [LastMonth] AS
IIF(
Day(Now()) = 1,
Subset([LastTwo], 1, 1),
Subset([LastTwo], 0, 1)
)
MEMBER [Measures].[LastMth_BillInc] AS
(
[LastMth].Item(0).Item(0),
[Measures].[BillableIncome]
)
MEMBER [Measures].[PrevMth_BillInc] AS
(
[LastMth].ITEM(0).Item(0).Lag(2),
[Measures].[BillableIncome]
)
SELECT
NON EMPTY
{
[Measures].[LastMth_BillInc],
[Measures].[PrevMth_BillInc]
}
ON COLUMNS,
NON EMPTY
ORDER(
[Customer_Dim].[Customer_Hier].[Customer_Level].Members,
[Date].[Date - Calendar Month].[LastMth],
BDESC
)
ON ROWS
FROM [ourCube]
I think you do not need sum, you can just use tuples as I did above, as there is only one member and not a set of several to be summed.