Can I use the Lag function against a custom member - mdx

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.

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 Running Totals with parameters

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]

Adjust TOPCOUNT to be time aware

Got the following script but it seems to pull back the top 15 customers by NumDeals that they have made over all time - rather than by the last 7 days.
How do I adjust the set to reflect this requirement?
WITH
SET [Set_7Days] as
TAIL ([Date].[Date - Calendar Month].[Calendar Day], 7)
SET [Set_Top15Customers] as
{
TOPCOUNT (
[CustomerDim].[CustomerDim].[CustomerLevel].members,
15,
(
[Date].[Date - Calendar Month].currentmember,
[Measures].[NumDeals]
)
)
}
SELECT
NON EMPTY
[Set_7Days]
ON COLUMNS,
NON EMPTY
[Set_Top15Customers]
ON ROWS
FROM [ourCube]
WHERE (
[Measures].[NumDeals]
)
EDIT
Using mmarie help I've got the following: wanted to keep things in a custom set so that I can add it to a PIVOT connected to a CUBE in EXCEL:
WITH
SET as
TAIL ([Date].[Date - Calendar Month].[Calendar Day], 7)
SET [Set_Top15Customers] as
{
TOPCOUNT (
[CustomerDim].[CustomerDim].[CustomerLevel].members,
15,
Sum(
[Set_7Days], //TAIL([Date].[Date - Calendar Month].[Calendar Day], 7),
[Measures].[NumDeals]
)
)
}
SELECT
NON EMPTY
[Set_7Days]
ON COLUMNS,
NON EMPTY
[Set_Top15Customers]
ON ROWS
FROM [ourCube]
WHERE (
[Measures].[NumDeals]
)
You need to add the date filter into your top count. There are probably a few ways to do this. This one works, but you might have to check performance to see if it is acceptable.
Try this:
with
member [measures].[Num Deals last 7 days] as
Sum(Head([Date].[Date - Calendar Month].[Calendar Day], 7), [Measures].[NumDeals])
select
NON Empty {TAIL ([Date].[Date - Calendar Month].[Calendar Day], 7)[measures].[Num Deals Last 7 days]} on 0,
NON Empty {topcount( {[CustomerDim].[CustomerDim].[CustomerLevel].members}, 5, measures.[Num Deals last 7 days])} on 1
FROM [ourCube]
WHERE ( [Measures].[NumDeals] )

multiple dimension restrictions in a MDX where clause

I have the following problem. If I query values with a keyfigure which is a function I can't specify multiple values of the same dimension restriction, but if it is not a function it works.
So this works:
SELECT {[Measures].[Netto]} on columns FROM TDC where
({NonEmpty([Time].[Month].[Month].&[2008-03-01T00:00:00]),
NonEmpty([Time].[Month].[Month].&[2008-04-01T00:00:00])})
But this doesn't:
SELECT {[Measures].[CalculatedFunction]} on columns FROM TDC where
({NonEmpty([Time].[Month].[Month].&[2008-03-01T00:00:00]),
NonEmpty([Time].[Month].[Month].&[2008-04-01T00:00:00])})
And this also works:
SELECT {[Measures].[CalculatedFunction]} on columns FROM TDC where
({NonEmpty([Time].[Month].[Month].&[2008-03-01T00:00:00])})
I guess the solution is something like adding the where clause to the header but I really like this solution because it's so simple.
The Calucated function is:
CREATE MEMBER CURRENTCUBE.[MEASURES].Ultimo
AS (iif ((not [Time].[Year - Month - Date].currentmember is [Time].[Year - Month - Date].defaultmember),
IIF(NOT ([Measures].[LagerStk] = 0),
Sum([Time].[Year - Month - Date].[Date].members(0):
ClosingPeriod([Time].[Year - Month - Date].[Date]),
[Measures].[LagerStk]), NULL)
,
IIF(NOT ([Measures].[LagerStk] = 0),
Sum([Time].[Year - Week - Date].[Date].members(0):
ClosingPeriod([Time].[Year - Week - Date].[Date]),
[Measures].[LagerStk]), NULL))),
VISIBLE = 1;
The code is inspired from this and modified for two hierarchies in the time dimension: http://www.sqlserveranalysisservices.com/OLAPPapers/InventoryManagement%20in%20AS2005v2.htm
This is on SQL server 2005 Enterprise edition.
Ok, this works:
WITH MEMBER [Time].[Month].a AGGREGATE
({[Time].[Month].[Month].&[2008-03-01T00:00:00],
[Time].[Month].[Month].&[2008-04-01T00:00:00]})
SELECT {[Measures].[CalculatedFunction]} on columns FROM TDC where a
The problem is in your calculated measure. You are using .CurrentMember and ClosingPeriod without a specific member reference which implies a call to .CurrentMember. When you have set in the WHERE clause there is no "Current" member - there are multiple current members. Re-writting your MDX to something like the following should allow it to work with multiple members in the WHERE clause.
CREATE
MEMBER CURRENTCUBE.[MEASURES].Ultimo AS NULL;
SCOPE ([MEASURES].Ultimo);
SCOPE ([Time].[Year - Month - Date].[All]);
this = IIF
(
(NOT
[Measures].[LagerStk] = 0)
,Sum
(
NULL:Tail(Existing [Time].[Year - Week - Date].[Date],1).item(0).item(0)
,[Measures].[LagerStk]
)
,NULL
);
END SCOPE;
SCOPE ([Time].[Year - Week - Date].[All]);
this = IIF
(
(NOT
[Measures].[LagerStk] = 0)
,Sum
(
NULL:Tail(Existing [Time].[Year - Month - Date].[Date],1).Item(0).Item(0)
,[Measures].[LagerStk]
)
,NULL
)
);
END SCOPE;
END SCOPE;
I am using SCOPE on the All members of the two dimensions, this should be fast that the out IIF and will also avoid one reference to .CurrentMember. I then replaced the ClosingPeriod() call with Tail(Existing [Time].[Year - Week - Date].[Date],1).item(0).item(0) what this does is to get the set of date members that exist in the current context, the Tail() call then gets the last on of these as a single member set and the .Item(0).Item(0) calls get the first member from the first tuple of that set.
Obviously, not having access to your cube, I can't test any of this. The issue you reported in your comment could relate to either an incorrect reference to the All members (I may have a different naming format to the one in your cube) or it may be related to the IIF() statement. I'm not sure that the check for 0 is being evaluated in the correct context
You could try testing without the IIF()
CREATE
MEMBER CURRENTCUBE.[MEASURES].Ultimo AS NULL;
SCOPE ([MEASURES].Ultimo);
SCOPE ([Time].[Year - Month - Date].[All]);
this = Sum
(
NULL:Tail(Existing [Time].[Year - Week - Date].[Date],1).item(0).item(0)
,[Measures].[LagerStk]
);
END SCOPE;
SCOPE ([Time].[Year - Week - Date].[All]);
this = Sum
(
NULL:Tail(Existing [Time].[Year - Month - Date].[Date],1).Item(0).Item(0)
,[Measures].[LagerStk]
);
END SCOPE;
END SCOPE;