Create Set to get Last month of Each Year - ssas

I would like to create a set that returned the last Date for each year. For example all previous years would return December, but the current year would only return the current month.
WITH
SET testset AS
NonEmpty
(
Generate
(
{
OpeningPeriod([Date].[Calendar].[Month])
:
ClosingPeriod([Date].[Calendar].Month)
}
,{[Date].[calendar].CurrentMember}
)
,[Measures].[Sale Amount]
)
SELECT
NON EMPTY
(
[Measures].[Sale Amount]
,[Date].[Year].[Year]
) ON 0
,NON EMPTY
[testset] ON 1
FROM [Cube]
Here is an example of a script that returns the values for each month. I've tried using tail and lastchild, but that only returns the most recent. I would like it to return for every Year.

In terms of just the last month - ignoring whether there is data for it or not the following:
WITH
SET [AllYears] AS
[Date].[Calendar].[Calendar Year].MEMBERS
SET [LastMths] AS
Generate
(
[AllYears] AS S
,Tail
(
Descendants
(
S.CurrentMember
,[Date].[Calendar].[Month]
)
,1
)
)
SELECT
{} ON 0
,[LastMths] ON 1
FROM [Adventure Works];
Returns this:
If I want to adapt the above so that it is the last month per year that has data for a specific measure then wrap NonEmpty around the set created by Descendants:
WITH
SET [AllYears] AS
[Date].[Calendar].[Calendar Year].MEMBERS
SET [LastMths] AS
Generate
(
[AllYears] AS S
,Tail
(
NonEmpty //<<new
(
Descendants
(
S.CurrentMember
,[Date].[Calendar].[Month]
)
,[Measures].[Internet Sales Amount] //<<new
)
,1
)
)
SELECT
{} ON 0
,[LastMths] ON 1
FROM [Adventure Works];
It now gives us this:
We can then add in the tuple you have on rows (I have used the attribute hierarchy this time for years as [Date].[Calendar] is already in use)
WITH
SET [AllYears] AS
[Date].[Calendar].[Calendar Year].MEMBERS
SET [LastMths] AS
Generate
(
[AllYears] AS S
,Tail
(
NonEmpty
(
Descendants
(
S.CurrentMember
,[Date].[Calendar].[Month]
)
,[Measures].[Internet Sales Amount]
)
,1
)
)
SELECT
NON EMPTY
(
[Measures].[Internet Sales Amount]
,[Date].[Calendar Year].[Calendar Year]
) ON 0
,[LastMths] ON 1
FROM [Adventure Works];
Now we get this:

#Whytheq has already given a very good solution. Treat this as an alternative. This might be a tad faster as it doesn't use the GENERATE function (not sure though!).
Have a calculated/cube measure which basically tells whether a month is the last month of the year. Then select those months out of the set of months.
with member measures.islastchild as
iif
(
[Date].[Calendar].currentmember is
[Date].[Calendar].currentmember.parent.parent.parent.lastchild.lastchild.lastchild,
1,
null
)
The member measures.islastchild returns 1 if the month is the last month of the year. Else it would return null.
set lastmonths as
filter(
[Date].[Calendar].[Month].members,
measures.islastchild = 1
)
The set lastmonths is then the set you need.
edit
To further improve the performance, you can NonEmpty function instead of the iterative FILTER function.
set lastmonths as
NonEmpty(
[Date].[Calendar].[Month].members,
measures.islastchild
)
select lastmonths on 1,
{} on 0
from [Adventure Works]
EDIT 2: To get the last non month with sales
with member measures.islastnonemptychild as
iif
(
[Date].[Calendar].currentmember is
TAIL(NonEmpty([Date].[Calendar].currentmember.parent.parent.parent.lastchild.lastchild.children, [Measures].[Sale Amount])).ITEM(0),
1,
null
)
set nonemptylastmonths as
NonEmpty(
[Date].[Calendar].[Month].members,
measures.islastnonemptychild
)

Related

I'm stuck with MDX query

i need sales amount of first month of each quarter
WITH SET [FIRSTMONTHOFQTR] AS
DESCENDANTS(
DESCENDANTS(
[Date].[Calendar].CURRENTMEMBER,
[Date].[Calendar].[Calendar Quarter]
),
[Date].[Calendar].[Month]
)
SELECT {
[Measures].[Sales Amount]
} ON COLUMNS,
{
[FIRSTMONTHOFQTR]
} ON ROWS
FROM [Adventure Works];
With above im getting each month but i need only first month. how can i filter that?
This is one approach:
WITH
SET [Qtrs] AS
[Date].[Calendar].[Calendar Quarter]
SET [FIRSTMONTHOFQTR] AS
Generate
(
[Qtrs] AS s
,Head(Descendants(s.CurrentMember,[Date].[Calendar].[Month]))
)
SELECT
{[Measures].[Sales Amount]} ON COLUMNS
,[FIRSTMONTHOFQTR] ON ROWS
FROM [Adventure Works];
It returns the following:

Ranking of multiple dimensions, restarting for every year

I have a measure, Sales Amount. I want to rank customers within a divison by year for that measure. I need to also display that rank as a measure. The rank needs to start over every year. I am able to do customers by year and customers by division, but I can't seem to figure out how to combine them both so it iterates over both dimensions properly. Below is what I have for the customers by year. I have tried adding another Division set, creating another named set that I GENERATE with the YearsWithCustomers set, and RANK using that new named set. I seem to be super close to figuring this out but I think I am putting something in the wrong place. I got the idea to iterate over a set from one of Chris Webb's blogs, located here.
WITH
SET Years AS
TopPercent
(
[Sales and Forecast Date].[Calendar Year].[Year Number].MEMBERS
,100
,[Measures].[Sales Amount]
)
SET Customers AS
Filter
(
[Customer].[Customer Number].[Customer Number].MEMBERS
,
[Measures].[Sales Amount] > 0
)
SET YearsWithCustomers AS
Generate
(
Years
,Union
(
{[Sales and Forecast Date].[Calendar Year].CurrentMember}
,StrToSet
("
Intersect({},
{order(Customers,([Sales Amount],[Sales and Forecast Date].[Calendar Year].CurrentMember),desc)
as CustomerSet"
+
Cstr(Years.CurrentOrdinal)
+ "})"
)
)
,ALL
)
MEMBER [Measures].[Customer Rank] AS
Rank
(
[Customer].[Customer Number].CurrentMember
,StrToSet
("CustomerSet"
+
Cstr
(
Rank
(
[Sales and Forecast Date].[Calendar Year].CurrentMember
,Years
)
)
)
)
SELECT
{
[Customer Rank]
,[Measures].[Sales Amount]
} ON 0
,Order
(
Filter
(
(
YearsWithCustomers
,Customers
)
,
[Sales Amount] > 0
)
,[Sales Amount]
,desc
) ON 1
FROM [OrdersAndBudgets];
Here is what I currently have. I would expect to see 1, 2, 3, etc for the Rank measure. It should reset for each division for every year.
I like this sort of pattern:
WITH
SET [AllCountries] AS
[Country].[Country].MEMBERS
SET [AllProds] AS
[Product].[Product].[Product].MEMBERS
SET [Top5Prods] AS
Generate
(
[AllCountries] AS a
,{
(
a.CurrentMember
,[Product].[Product].[All]
)
+
//The top x prods
a.CurrentMember
*
TopCount
(
[AllProds]
,5
,[Measures].[Internet Sales Amount]
)
}
)
MEMBER [Product].[Product].[All].[Other Products] AS
Aggregate
(
[Country].CurrentMember * [Product].[Product].[Product].MEMBERS
-
[Top5Prods]
)
SELECT
{[Measures].[Internet Sales Amount]} ON COLUMNS
,Hierarchize(
{
[Top5Prods]
,[AllCountries] * [Product].[Product].[All].[Other Products]
}
) ON ROWS
FROM [Adventure Works];
It returns the following:
There is quite an extensive thread here: Top X of Top Y with RestOf member where X and Y are hierarchies from different dimensions

MDX return last month of every year

I am trying to create a set to return the value of the last month that contains data for each year.
For example if I would put year on the rows
2011,2012,2013,2014 would all contain data for December.
2015 would contain data for June of 2015.
I can't seem to get anything but the latest month to return. I figure it is because of my tail statement, but I'm not sure how to fix it.
CREATE SET [Last Statement Month] AS
Tail(
nonempty(
Descendants(
[Date].[Calendar].currentmember
,[Date].[Calendar].[Month]
),
[Measures].[Sale Amount]
), 1);
I also tried to get the last day of each month, but when I use this with the year on the rows nothing shows up.
GENERATE(
{
Openingperiod([Date].[Calendar].[Month]):ClosingPeriod([Date].[Calendar].Month)
},
{[Date].[Calendar].CurrentMember.Lastchild}
);
I'm currently away from AdvWrks so unable to test. Does the following help?
CREATE SET [Last Statement Month] AS
TAIL(
NONEMPTY(
EXISTING ([Date].[Calendar].[Month].MEMBERS)
,[Measures].[Sale Amount]
)
);
(If this approach works) Performance is apparently better if EXISTING is performed last:
CREATE SET [Last Statement Month] AS
TAIL(
EXISTING
NONEMPTY(
[Date].[Calendar].[Month].MEMBERS
,[Measures].[Sale Amount]
)
);
Looks like the above isn't going to work. I've added an alternative in the following which maybe is more what you're looking for:
WITH
DYNAMIC SET [Last Statement Month] AS
Tail
(
NonEmpty
(
(EXISTING
[Date].[Calendar].[Month].MEMBERS)
,[Measures].[Internet Sales Amount]
)
)
MEMBER [Measures].[x] AS
[Last Statement Month].Item(0).Item(0).Member_Caption
MEMBER [Measures].[Lst mth with data] AS `<<<<maybe something like this helps?
Max
(
(EXISTING
[Date].[Calendar].[Month].MEMBERS)
,IIF
(
[Measures].[Internet Sales Amount] = 0
,NULL
/*,[Date].[Calendar].CurrentMember.Member_Caption*/ //<<WRONG PROPERTY USED
,[Date].[Calendar].CurrentMember.MemberValue //<<should help!!
)
)
SELECT
{[Measures].[Lst mth with data],[Measures].[x]} ON 0
,[Date].[Calendar].[Calendar Year] ON 1
FROM [Adventure Works];
Results in this:
After Edit returns this:

Moving Average of Last 24 months

I have this calculated member which calculates a moving average for the last 12 months:
iif(IsEmpty(Sum({[Time].[Month].CurrentMember:NULL},
[Measures].[Count])), NULL,
Avg
(
[Time].[Month].CurrentMember.Lag(11) :
[Time].[Month].CurrentMember,
[Measures].[Count]
))
The iif condition is in place because I don't want to get values for future months (with no value), which I do get without it.
What I want to do is have this measure only for the last 24 months since the last not empty month.
I've tried with Tail and Lag but with no luck (I would post my attempts here but after many tries I deleted them and would really not know where to begin again).
Thanks to #whytheq this is the final solution that I used:
CREATE DYNAMIC SET CURRENTCUBE.[FirstEmptyMonth]
AS { Tail
(
NonEmpty
(
[Time].[Month].MEMBERS
,[Measures].[Count]
)
,1
).Item(0).NextMember };
CREATE DYNAMIC SET CURRENTCUBE.[MonthsToIgnore]
AS {[FirstEmptyMonth].Item(0) : NULL}
+
{NULL : [FirstEmptyMonth].Item(0).Lag(25)} ;
CREATE MEMBER CURRENTCUBE.[Measures].[Moving Average]
AS IIF
(
Intersect({[Time].[Month].CurrentMember},[MonthsToIgnore]).Count = 1
,null
,Avg
(
[Time].[Month].CurrentMember.Lag(11) : [Time].[Month].CurrentMember
,[Measures].[Count]
)
);
In AdvWrks I've got this:
WITH
SET [FutureMonthsWithNoData] AS
{
Tail
(
NonEmpty
(
[Date].[Calendar].[Month].MEMBERS
,[Measures].[Internet Sales Amount]
)
,1
).Item(0).NextMember
: NULL
}
MEMBER [Measures].[blah] AS
IIF
(
Intersect
(
{[Date].[Calendar].CurrentMember}
,[FutureMonthsWithNoData]
).Count
= 1
,null
,1
)
SELECT
{
[Measures].[Internet Sales Amount]
,[Measures].[blah]
} ON 0
,[Date].[Calendar].[Month].MEMBERS ON 1
FROM [Adventure Works];
It returns this:
So what I am saying is that you could create this initial set of FutureDatesWithNoData and then use that set to create a condition within your script. The set would be (I think) this in your cube:
SET [FutureMonthsWithNoData] AS
{
Tail
(
NonEmpty
(
[Time].[Month].[Month].MEMBERS
,[Measures].[Count]
)
,1
).Item(0).NextMember
: NULL
}
Your measure would then be as follows:
IIF
(
Intersect
(
{[Time].[Month].CurrentMember}
,[FutureMonthsWithNoData]
).Count
= 1
,NULL
,Avg
(
[Time].[Month].CurrentMember.Lag(11) : [Time].[Month].CurrentMember
,[Measures].[Count]
)
)
If you want to also exclude months prior to 24 months ago then this script sums up the logic:
WITH
SET [FistEmptyMonth] AS
{
Tail
(
NonEmpty
(
[Date].[Calendar].[Month].MEMBERS
,[Measures].[Internet Sales Amount]
)
,1
).Item(0).NextMember
}
SET [MonthsToIgnore] AS
{[FistEmptyMonth].Item(0) : NULL}
+
{NULL : [FistEmptyMonth].Item(0).Lag(24)}
MEMBER [Measures].[blah] AS
IIF
(
Intersect({[Date].[Calendar].CurrentMember},[MonthsToIgnore]).Count = 1
,null
,1
)
SELECT
{[Measures].[Internet Sales Amount]} ON 0
,[Date].[Calendar].[Month].MEMBERS ON 1
FROM [Adventure Works];

MDX Start and End Time per transaction

I hope you can help i have tried so many way to try get this right with no luck. I am trying to get out the player account number the date and start and end date time and maybe calculate the play duration between the start and end times.
I would like the output to look something like this.
PlayerAccount | GamingDate | StartTime | EndTime | PlayDuration | ActualWin
I always seem to return the start and end time for the whole day and not Per account.
WITH
SET [MySet] AS
[Customer].[Player Account Number].Children*
Head
(
NonEmpty
(
[Start Time].[Hour].Children
,[Measures].[Actual Win]
)
,1
)*
Tail
(
NonEmpty
(
[End Time].[Hour].Children
,[Measures].[Actual Win]
)
,1
)
SELECT
{[Measures].[Actual Win]} ON 0
,{[MySet]} ON 1
FROM
(
SELECT
[Customer].[Player Account Number].&[1040002184]
:
[Customer].[Player Account Number].&[1040002198] ON 0
FROM Ratings
)
WHERE
{[Gaming Date].[Full Date].&[20150101]};
Usually first date and last date would be measures. If you only want results for those two player then use the WHERE clause rather than a sub-select:
WITH
MEMBER [Measures].[fDate] AS
Head
(
NonEmpty
(
[Start Time].[Hour].MEMBERS
,[Measures].[Actual Win]
)
).Item(0).Item(0).Member_Caption
MEMBER [Measures].[lDate] AS
Tail
(
NonEmpty
(
[End Time].[Hour].MEMBERS
,[Measures].[Actual Win]
)
).Item(0).Item(0).Member_Caption
SELECT
{
[Measures].[fDate]
,[Measures].[lDate]
,[Measures].[Actual Win]
} ON 0
,{[Customer].[Player Account Number].Children} ON 1
FROM Ratings
WHERE
([Gaming Date].[Full Date].&[20150101],
{ [Customer].[Player Account Number].&[1040002184]
,[Customer].[Player Account Number].&[1040002198]});
This is a working AdvWrks script which does the sort of thing you're trying to achieve:
WITH
MEMBER [Measures].[firstDate] AS
Head
(
NonEmpty
(
[Date].[Date].[Date].MEMBERS
,[Measures].[Internet Sales Amount]
)
).Item(0).Item(0).Member_Caption
MEMBER [Measures].[lastDate] AS
Tail
(
NonEmpty
(
[Date].[Date].[Date].MEMBERS
,[Measures].[Internet Sales Amount]
)
).Item(0).Item(0).Member_Caption
SELECT
{
[Measures].[Internet Sales Amount]
,[Measures].[firstDate]
,[Measures].[lastDate]
} ON 0
,NON EMPTY {[Promotion].[Promotion].MEMBERS} ON 1
FROM [Adventure Works];
If you'd rather pull back a member instead of using Measures I think the GENERATE function will work like this AdvWrks example:
WITH
SET [aSet] AS
Generate
(
[Promotion].[Promotion].MEMBERS
,
[Promotion].[Promotion].CurrentMember
*
Head
(
NonEmpty
(
[Date].[Date].[Date].MEMBERS
,[Measures].[Internet Sales Amount]
)
)
)
SELECT
{[Measures].[Internet Sales Amount]} ON 0
,NON EMPTY
{[aSet]} ON 1
FROM [Adventure Works];
Thanks a million whytheq it seems like that .Item(0).Item(0).Member_Caption made all the difference the results seem to be come out right now. The final solution looks like this now..
with set [MySet]
as
[Casino Hierarchy].[Casino Key].children *
[Gaming Date].[Date].currentmember *
[Customer].[Player Account Number].children *
[Start Time].[Hour].children
member [measures].[Mindate]
as
HEAD(nonempty([End Time].[Hour].children,[Measures].[Actual Win]),1).Item(0).Item(0).Member_Caption
member [measures].[Maxdate]
as
TAIL(nonempty([Start Time].[Hour].children,[Measures].[Actual Win]),1).Item(0).Item(0).Member_Caption
select non empty
{
[Measures].[Actual Win],[measures].[Mindate],[measures].[Maxdate]
} on 0, non empty
{
[Customer].[Player Account Number].children
} on 1
from
(
select ([Customer].[Player Account Number].&[1040002184]:[Customer].[Player Account Number].&[1040002198]) on 0 from Ratings
)
where
{
[Gaming Date].[Full Date].&[20150101]
}
You are having a static set(which always return the same members). Use the EXISTING keyword to evaluate the Start and end time in the current context.
with member [Measures].[StartDate] as
EXISTING
HEAD(
nonempty(
[Start Time].[Hour].Children,[Measures].[Actual Win]
) ,1
).ITEM(0).ITEM(0).MEMBERVALUE
member [Measures].[EndDate] as
EXISTING
TAIL(
nonempty(
[End Time].[Hour].children,[Measures].[Actual Win]
) ,1
).ITEM(0).ITEM(0).MEMBERVALUE
select
{
[Measures].[Actual Win],
[Measures].[StartDate],
[Measures].[EndDate]
} on 0,
[Customer].[Player Account Number].children on 1
from
(
select (
[Customer].[Player Account Number].&[1040002184]:
[Customer].[Player Account Number].&[1040002198]
) on 0
from Ratings
)
where {[Gaming Date].[Full Date].&[20150101]}
Have rearranged the [Customer].[Player Account Number] so that dates are evaluated in the context of each player account. Hope that's fine!
Edit.
A simplified AdWrks version of this approach is as follows: still not quite working:
WITH
SET [aSet] AS
(EXISTING
Head
(
NonEmpty
(
[Date].[Date].[Date].MEMBERS
,[Measures].[Internet Sales Amount]
)
))
SELECT
{[Measures].[Internet Sales Amount]} ON 0
,NON EMPTY
{[Promotion].[Promotion].MEMBERS * [aSet]} ON 1
FROM [Adventure Works];