MDX Running Totals with parameters - mdx

I am trying to calculate running totals for a date range which is passed through parameters in SSRS. I know i need to use lag function, but i am not able to get it right. I tried using the following
WITH MEMBER [Measures].[Rolling12Months] AS
SUM(
[Reporting Period].[Fiscal].[Fiscal Month Name].&[1]&[2013] : [Reporting Period].[Fiscal].CURRENTMEMBER,
[Measures].[Amount]
)

Below script will do what #whytheq's script does, only dynamically. It figures out the last 12 months based on the current member, which you can pass on as a slicer, or when you are in a set context, you might not need the slicer axis. I am assuming that's why you thought you needed the LAG function.
WITH SET Last12Months AS
{[Reporting Period].[Fiscal].CURRENTMEMBER.LAG(12) : [Reporting Period].[Fiscal].CURRENTMEMBER}
MEMBER [Measures].[Rolling12Months] AS
SUM(
Last12Months,
[Measures].[Amount]
)
SELECT
[Measures].[Rolling12Months] ON 0
, [Reporting Period].[Fiscal].[Fiscal Month Name].Members ON 1
FROM [YourCubeName]
WHERE [Reporting Period].[Fiscal].[Fiscal Month Name].&[1]&[2014]
//`WHERE` clause can be left out if context is already set.

Your script looks ok - maybe just change to Aggregate:
WITH MEMBER [Measures].[Rolling12Months] AS
Aggregate(
[Reporting Period].[Fiscal].[Fiscal Month Name].&[1]&[2013]:[Reporting Period].[Fiscal].CurrentMember,
[Measures].[Amount]
)
So a complete script would be something like
WITH MEMBER [Measures].[Rolling12Months] AS
Aggregate(
[Reporting Period].[Fiscal].[Fiscal Month Name].&[1]&[2013]:[Reporting Period].[Fiscal].CurrentMember,
[Measures].[Amount]
)
SELECT
{[Measures].[Amount]
, [Measures].[Rolling12Months] } ON 0
, [Reporting Period].[Fiscal].[Fiscal Month Name].Members ON 1
FROM [YourCubeName]

Related

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.

MDX YTD over range (:)

Does YTD can handle date range ?
I'm trying to compare a period of X days of the current year with the same period of the prior year.
My MDX query looks like this :
WITH MEMBER [Measures].[Prior YTD Amount] AS SUM
(
YTD
(
ParallelPeriod
(
[Date].[Year],
1,
[Date].CurrentMember
)
)
,
[Measures].[Amount]
)
set [ColSet] as
{
[Measures].[Amount],
[Measures].[Prior YTD Amount]
}
set [RowSet] as
{
[Motif].[Categorie].Members
}
SELECT
NON EMPTY [ColSet] ON COLUMNS,
NON EMPTY [RowSet] ON ROWS
FROM [Things]
WHERE
{
[Date].[2015].[2].[1]:[Date].[2015].[2].[9]
}
I'm working with Mondrian 3.6.
In short this code will not work. Reason being that the following line of code is looking for a single member of the hierarchy you've applied the Currentmember to:
[Date].CurrentMember
You have more than one member in the set [Date].[2015].[2].[1]:[Date].[2015].[2].[9]
Here is a small proof of the above:
This script:
WITH
MEMBER [Measures].[x] AS
[Date].[Calendar].CurrentMember.Member_Key
SELECT
{[Product].[Category].[All Products]} ON 0
,{[Measures].[x]} ON 1
FROM [Adventure Works]
WHERE
{
[Date].[Calendar].[Date].&[20080322]
};
Returns this:
Whereas this script:
WITH
MEMBER [Measures].[x] AS
[Date].[Calendar].CurrentMember.Member_Caption
SELECT
{[Product].[Category].[All Products]} ON 0
,{[Measures].[x]} ON 1
FROM [Adventure Works]
WHERE
{
[Date].[Calendar].[Date].&[20080322]
:
[Date].[Calendar].[Date].&[20080323]
};
Returns this:
Error message:
In AdvWrks I just wrote the following which might help, if you can move your target range of dates from the WHERE clause to a named set.
How it works:
1.Finds the first date in your named set via .Item(0).Item(0)
2.Finds the last date in your named set via .Item([myStartDates].Count - 1).Item(0)
3.The uses the two dates found to create a range that is parallel
WITH
SET [myStartDates] AS
{
[Date].[Calendar].[Date].&[20080322]
:
[Date].[Calendar].[Date].&[20080323]
}
SET [parallelToFirst] AS
ParallelPeriod
(
[Date].[Calendar].[Calendar Year]
,1
,[myStartDates].Item(0).Item(0)
)
SET [parallelToLast] AS
ParallelPeriod
(
[Date].[Calendar].[Calendar Year]
,1
,[myStartDates].Item(
[myStartDates].Count - 1).Item(0)
)
SET [Rebuild] AS
[parallelToFirst].Item(0) : [parallelToLast].Item(0)
SELECT
{} ON COLUMNS
,[Rebuild] ON ROWS
FROM [Adventure Works];
Easy enough to then Sum that range and pop it in the date dimension:
WITH
SET [myStartDates] AS
{
[Date].[Calendar].[Date].&[20080322]
:
[Date].[Calendar].[Date].&[20080323]
}
SET [parallelToFirst] AS
ParallelPeriod
(
[Date].[Calendar].[Calendar Year]
,1
,[myStartDates].Item(0).Item(0)
)
SET [parallelToLast] AS
ParallelPeriod
(
[Date].[Calendar].[Calendar Year]
,1
,[myStartDates].Item(
[myStartDates].Count - 1).Item(0)
)
SET [ParallelRange] AS
[parallelToFirst].Item(0) : [parallelToLast].Item(0)
MEMBER [Date].[Calendar].[parallelSum] AS
Sum([ParallelRange])
SELECT
{
[myStartDates]
,[ParallelRange]
,[Date].[Calendar].[parallelSum]
} ON 0
,{
[Measures].[Internet Sales Amount]
,[Measures].[Internet Order Quantity]
} ON 1
FROM [Adventure Works];
Here is the result (just to prove it is doing what we want):
Below is an alternative approach, to using ParallelPeriods function.
I am assuming you have all the dates from prev year in the cube, i.e. there are no missing dates in between or "gaps". If so, you can use the LAG function to hop over to the date 365 days(1 year) back. Also, have the dates from current year initially in a set. That way you can parametrize them easily.
Your final MDX should look somewhat similar to:
with set Dates as
{
[Date].[2015].[2].[1]
:
[Date].[2015].[2].[9]
}//<<This can be also written as
//StrToSet("{[Date]."+ #DateStart + ":" + "[Date]."+ #DateEnd + "}")
//#Date1 and #Date2 being the parameters you can pass from front end
set datesPrevYear as
{
head(Dates, 1).item(0).LAG(365).item(0) \\<<Gets the starting date's 1 year previous date
:
tail(Dates, 1).item(0).LAG(365).item(0) \\<<Gets the ending date's 1 year previous date
}
member [Measures].[Prior YTD Amount] as
sum(datesPrevYear, [Measures].[Amount])
set [ColSet] as
{
[Measures].[Amount],
[Measures].[Prior YTD Amount]
}
set [RowSet] as
{
[Motif].[Categorie].Members
}
select
non empty [ColSet] on columns,
non empty [RowSet] on rows
from [Things]

MDX: Mixed row aggregation types within single period column time aggregation

I am trying wrap my head around a way to produce the following result from a Mondrian cube.
Sample Values:
Year Month Sales
---- ----- -----
2015 Jan 10
2015 Feb 11
2015 Mar 12
2015 Apr 10
2015 May 11
2015 Jun 12
Jan-Mar 2015 | Apr-Jun 2015
---------------------------------------------------
Sales Sum | 33 | 33
Sales Average | 11 | 11
The current MDX is something like this:
with
member [Date].[JAN-MAR] as Aggregate([Date].[2015].[3].lag(2):[Date].[2015].[3])
member [Date].[APR-JUN] as Aggregate([Date].[2015].[6].lag(2):[Date].[2015].[6])
member [Measures].[Sales Sum] as Sum([Date].CurrentMember, [Measures].[Sales])
member [Measures].[Sales Average] as Avg([Date].CurrentMember, [Measures].[Sales])
select
{[Date].[JAN-MAR],
[Date].[APR-JUN]} on columns,
{[Measures].[Sales Sum],
[Measures].[Sales Average]} on rows
from [Cube]
The question is how can I get a row to specify an aggregate to use for the current column period aggregation?
Update (17 Aug 2018)
I think I have found a solution, before I get into that I think I should give more background into the scenario. We are using Mondrian to provide some financial reports. Due to the complexity of the reports combined with the fact that end users must be able to create them we have created our own mini reporting tool.
One of the most common report types is measures on rows and columns with various date aggregations e.g. Three Month Rolling Average / Financial Year to Date etc all based on a report parameter date selection offset.
The complexity comes in where for the same column they want different rows to aggregate differently. An example would be the Financial Year to Date column, some rows measures must be summed, some must be averaged and some must return the closing balance.
I haven't found an easy want to model this in the cube yet :/
However I found a way to get it to work by mistake that seems relevantly robust and is also fast. As it turns out Mondrian does not validate member attributes, i.e. you can declare and reference whatever member attributes you want. This has turned out to provide an easy way to can get access to the correct date slice and perform whatever aggregate I want e.g:
with
member [Date].[JAN-MAR] as Aggregate([Date].[2015].[3].lag(2):[Date].[2015].[3]), START_MONTH_MEMBER='[Date].[2015].[1]', END_MONTH_MEMBER='[Date].[2015].[3]'
member [Date].[APR-JUN] as Aggregate([Date].[2015].[6].lag(2):[Date].[2015].[6]), START_MONTH_MEMBER='[Date].[2015].[4]', END_MONTH_MEMBER='[Date].[2015].[6]'
member [Measures].[Sales Sum] as Sum([Date].CurrentMember, [Measures].[Sales])
member [Measures].[Sales Average] as Avg(StrToMember([Date].CurrentMember.Properties('START_MONTH_MEMBER')):StrToMember([Date].CurrentMember.Properties('END_MONTH_MEMBER')), [Measures].[Sales])
select
{[Date].[JAN-MAR],
[Date].[APR-JUN]} on columns,
{[Measures].[Sales Sum],
[Measures].[Sales Average]} on rows
from [Cube]
So far this works well. One thing that doesn't work is I cannot get StrToSet to work. In theory you should be able to declare a set in the with member property and then use the in the measure.
StrToMember(([Date].CurrentMember.Properties('MONTH_RANGE_SET'))
So this what I have working for now, would love some feedback on that?
This is a bit time consuming, but should work:
with
member [Date].[JAN-MAR] as Aggregate([Date].[2015].[3].lag(2):[Date].[2015].[3])
member [Date].[APR-JUN] as Aggregate([Date].[2015].[6].lag(2):[Date].[2015].[6])
member [Measures].[Sales Sum] as Sum([Date].CurrentMember, [Measures].[Sales])
member [measures].yearvalues as [Date].currentmember.member_value
member [Measures].[Sales Average] as
AVG
(
StrToSet(
"[Date].[2015].&[" +
CASE
LEFT(measures.yearvalues, 3)
WHEN "JAN" THEN 1
WHEN "APR" THEN 4 END +
"]:[Date].[2015].&[" +
CASE
RIGHT(measures.yearvalues, 3)
WHEN "MAR" THEN 3
WHEN "JUN" THEN 5 END +
"]"
)
,
[Measures].[Sales]
),
format_string = "#.##"
select
{[Date].[JAN-MAR],
[Date].[APR-JUN]} on columns
{[Measures].[Sales Sum],
[Measures].[Sales Average]} on columns
from [Cube]
Far from ideal but best I can do at the moment:
WITH
SET [JAN-MAR] AS
[Date].[Calendar].[Month].&[2006]&[3].Lag(2)
:
[Date].[Calendar].[Month].&[2006]&[3]
SET [APR-JUN] AS
[Date].[Calendar].[Month].&[2006]&[6].Lag(2)
:
[Date].[Calendar].[Month].&[2006]&[6]
MEMBER [Date].[Calendar].[JAN-MAR] AS
Aggregate([JAN-MAR])
MEMBER [Date].[Calendar].[APR-JUN] AS
Aggregate([APR-JUN])
MEMBER [Measures].[Sales Sum] AS
[Measures].[Internet Sales Amount]
MEMBER [Measures].[Sales Average] AS
[Measures].[Internet Sales Amount] / [JAN-MAR].Count
SELECT
{
[Date].[Calendar].[JAN-MAR]
,[Date].[Calendar].[APR-JUN]
} ON 0
,{
[Measures].[Sales Sum]
,[Measures].[Sales Average]
} ON 1
FROM [Adventure Works];
So I thought maybe I'd try adding the custom members to an unrelated dimension (effectively make it a utility dimension). This works ok but extracting the count of number of related months is still proving difficult. This is the current effort:
WITH
SET [JAN-MAR] AS
[Date].[Calendar].[Month].&[2006]&[3].Lag(2)
:
[Date].[Calendar].[Month].&[2006]&[3]
SET [APR-JUN] AS
[Date].[Calendar].[Month].&[2006]&[6].Lag(2)
:
[Date].[Calendar].[Month].&[2006]&[6]
MEMBER [Product].[Category].[JAN-MAR] AS
Aggregate
(
[JAN-MAR]
,[Product].[Category].[All Products]
)
MEMBER [Product].[Category].[APR-JUN] AS
Aggregate
(
[APR-JUN]
,[Product].[Category].[All Products]
)
MEMBER [Measures].[Sales Sum] AS
[Measures].[Internet Sales Amount]
MEMBER [Measures].[Sales Avg] AS
[Measures].[Internet Sales Amount]
/
NonEmpty
(
[Date].[Calendar].[Month].MEMBERS
,(
[Product].[Category].CurrentMember
,[Measures].[Internet Sales Amount]
)
).Count //<<<<currently returning 72 rather than 3
SELECT
{
[Product].[Category].[JAN-MAR]
,[Product].[Category].[APR-JUN]
} ON 0
,{
[Measures].[Sales Sum]
,[Measures].[Sales Avg]
} ON 1
FROM [Adventure Works];
We can see that it is getting divided by 72 rather than 3:
Problem as I currently see it is trying to get hold of the number of related months to each of the custom members after they have been aggregated - here is a simplified example of what I mean:
WITH
SET [JAN-MAR] AS
//<< set of 3 months
[Date].[Calendar].[Month].&[2006]&[1]
:
[Date].[Calendar].[Month].&[2006]&[3]
MEMBER [Product].[Category].[JAN-MAR] AS
//<< chuck on unconnected hierarchy
Aggregate
(
[JAN-MAR]
,[Product].[Category].[All Products]
)
MEMBER [Measures].[countMonthsRelatedToMember] AS //<<attempt to count mths related to [Product].[Category].[JAN-MAR]
NonEmpty
(
[Date].[Calendar].[Month].MEMBERS
,(
[Product].[Category].CurrentMember
,[Measures].[Internet Sales Amount]
)
).Count //<<<<currently returning 72 rather than 3
SELECT
[Product].[Category].[JAN-MAR] ON 0
,[Measures].[countMonthsRelatedToMember] ON 1
FROM [Adventure Works];

Unable to retrieve the month from date parameter using vba function in mdx

I am trying to retrieve month from parametrized date , by using :
vba!month(${parDate}) or
vba!datePart(m,${parDate}) or
vba!format(${parDate},'mmm')
None of the above is working.Can you guide, what is the right approach to do this?
(try uppercase 'MM' rather than 'mmm' - even 'mm' is wrong as it will look for minutes rather than months)
This question and answer looks at working with dates:
Changing a date format to a shorter date
MSDN is a good reference for the available vba functions in mdx that you can use to play around with dates. Current link is here:
http://msdn.microsoft.com/en-us/library/hh510163.aspx
I'm assuming you have a date dimension and would like to create a calculated measure that returns a numeric value that is the month.
Using AdWks I can do the following:
WITH
MEMBER [Measures].[DateValue] AS
[Date].[Calendar].CurrentMember.MemberValue
MEMBER [Measures].[DateKey] AS
[Date].[Calendar].CurrentMember.Member_Key
MEMBER [Measures].[DateMONTH] AS
Mid
(
[Measures].[DateKey]
,5
,2
)
SELECT
{
[Measures].[DateValue]
,[Measures].[DateKey]
,[Measures].[DateMONTH]
} ON 0
,Order
(
{
Exists
(
[Date].[Date].MEMBERS
,[Date].[Calendar Year].&[2010]
)
}
,[Date].[Calendar].CurrentMember.MemberValue
,BDESC
) ON 1
FROM [Adventure Works];
But maybe you'd just like to play around with today's date and extract the month:
WITH
MEMBER [Measures].[DateValue] AS
[Date].[Calendar].CurrentMember.MemberValue
MEMBER [Measures].[TodayKey] AS
format(Now(),'yyyMMdd')
MEMBER [Measures].[TodayMONTH] AS
Mid
(
[Measures].[TodayKey]
,5
,2
)
SELECT
{
[Measures].[DateValue]
,[Measures].[TodayKey]
,[Measures].[TodayMONTH]
} ON 0
,Order
(
{
Exists
(
[Date].[Date].MEMBERS
,[Date].[Calendar Year].&[2010]
)
}
,[Date].[Calendar].CurrentMember.MemberValue
,BDESC
) ON 1
FROM [Adventure Works];

Is there a better way to calculate the moving average in an MDX in iccube?

I'm using the following calculated member to calculate the moving average for my visits for the last 30 days period; is there a shorter way to do this?
WITH
MEMBER [Measures].[Visits Moving Avg] AS
AVG(
[TIME].[Time].Prevmember : [TIME].[Time].Prevmember.Prevmember.Prevmember....
, [Measures].[VISITS]
), SOLVE_ORDER = 0
Instead of using the serie of prevMember.prevMember... calls you can use the Lag MDX function function as following :
WITH
MEMBER [Measures].[Visits Moving Avg] AS
AVG(
[TIME].[Time].prevMember : [TIME].[Time].prevMember.lag(30)
, [Measures].[VISITS]
), SOLVE_ORDER = 0
By the way, it looks like currentMember is missing in your query; you are currently computing the moving average for the defaultMember of the [Time] dimension. The query using the currentMember of the time dimension is as following :
WITH
MEMBER [Measures].[Visits Moving Avg] AS
AVG(
[TIME].[Time].currentMemBer.prevMember
: [TIME].[Time].currentMemBer.prevMember.lag(30)
, [Measures].[VISITS]
), SOLVE_ORDER = 0