Reputation: 1127
This is from a heavily manually maintained report that I am trying to automate a bit view an SSAS cube.
The report contains daily sales, and, among other things, a measure called "last 4 's". E.g., For Friday, October 16 the measure was the average sales over the last 4 Fridays.
Is there a way to construct this in MDX in way that can be placed in a calculated measure in an SSAS cube?
ps--In response to whytheq's question, yes, the date dimension includes day of week, which is an integer in which Sun = 1, Mon = 2 and so on to Sat = 7.
I see that I was slightly ambiguous above. By "last 4 Fridays" above I meant the 4 Fridays immediately preceding October 16, not the most recent 4 Fridays.
Upvotes: 1
Views: 1886
Reputation: 35557
If you have a hierarchy within the date dimension that indicates Day of the Week then you should use it in the sort of circumstance of this question.
Via AdvWrks
I wrote the following:
WITH
MEMBER [Measures].[Avg4wkDays] AS
Sum
(
Tail
( --<<find just the past 4
Exists
( --<< find all the days from the past 25 days which have the same day of the week as the current date
Tail
( --<< find the last 25 days prior to each date
NULL : [Date].[Calendar].CurrentMember
,25
)
,Exists
( --<< find the Day of the Week for the currentmember
[Date].[Day of Week].[Day of Week].MEMBERS
,[Date].[Calendar].CurrentMember
)
)
,4
)
,[Measures].[Internet Sales Amount]
)
SELECT
{
[Measures].[Internet Sales Amount]
,[Measures].[Avg4wkDays]
} ON 0
,
[Date].[Calendar].[Date] * [Date].[Day of Week].[Day of Week] ON 1
FROM [Adventure Works]
WHERE
[Date].[Calendar Year].&[2007];
It gives the following which agrees with the requirements:
Upvotes: 0
Reputation: 1515
Agreed with whytheq, that more information may help us to create optimal solution. Anyway:
Solved if you have only flat days hierarchy:
+All
-2015/01/01
-2015/01/02
...
-2015/12/31
...
Logic could be like this:
rank all days
split by weeks
calculate last 4 for every day type
show result for every selected member
Example of flat hierarchy [Report Date].[Report Date].[Day] calculation:
with
member [Measures].[AllDaysRank] as Rank([Report Date].[Report Date].CurrentMember,[Report Date].[Report Date].[Day].Members)
member [Measures].[WeekDay] as ([Measures].[AllDaysRank]-(Int([Measures].[AllDaysRank]/7)*7))
set [Last4Set0] as TopCount(Filter([Report Date].[Report Date].[Day].Members,[Measures].[WeekDay]=0),4,[Measures].[AllDaysRank])
set [Last4Set1] as TopCount(Filter([Report Date].[Report Date].[Day].Members,[Measures].[WeekDay]=1),4,[Measures].[AllDaysRank])
set [Last4Set2] as TopCount(Filter([Report Date].[Report Date].[Day].Members,[Measures].[WeekDay]=2),4,[Measures].[AllDaysRank])
set [Last4Set3] as TopCount(Filter([Report Date].[Report Date].[Day].Members,[Measures].[WeekDay]=3),4,[Measures].[AllDaysRank])
set [Last4Set4] as TopCount(Filter([Report Date].[Report Date].[Day].Members,[Measures].[WeekDay]=4),4,[Measures].[AllDaysRank])
set [Last4Set5] as TopCount(Filter([Report Date].[Report Date].[Day].Members,[Measures].[WeekDay]=5),4,[Measures].[AllDaysRank])
set [Last4Set6] as TopCount(Filter([Report Date].[Report Date].[Day].Members,[Measures].[WeekDay]=6),4,[Measures].[AllDaysRank])
member [Measures].[Last4Measure] as
case [Measures].[WeekDay]
when 0 then sum([Last4Set0],[Measures].[Count])
when 1 then sum([Last4Set1],[Measures].[Count])
when 2 then sum([Last4Set2],[Measures].[Count])
when 3 then sum([Last4Set3],[Measures].[Count])
when 4 then sum([Last4Set4],[Measures].[Count])
when 5 then sum([Last4Set5],[Measures].[Count])
when 6 then sum([Last4Set6],[Measures].[Count])
end
select {[Measures].[Count],[Measures].[AllDaysRank],[Measures].[WeekDay],[Measures].[Last4Measure]} on 0
,[Report Date].[Report Date].[Day].Members on 1
from [DATA]
Result (Count, AllDaysRank, WeekDay, Last4Measure):
20151001 10 740 5 35
20151002 10 741 6 39
20151003 8 742 0 37
20151004 12 743 1 42
20151005 13 744 2 42
20151006 12 745 3 39
20151007 10 746 4 36
20151008 8 747 5 35
20151009 6 748 6 39
20151010 11 749 0 37
20151011 10 750 1 42
20151012 7 751 2 42
20151013 8 752 3 39
20151014 6 753 4 36
20151015 9 754 5 35
20151016 11 755 6 39
20151017 11 756 0 37
20151018 10 757 1 42
20151019 14 758 2 42
20151020 8 759 3 39
20151021 11 760 4 36
20151022 4 761 5 35
20151023 16 762 6 39
20151024 5 763 0 37
20151025 10 764 1 42
20151026 8 765 2 42
20151027 11 766 3 39
20151028 9 767 4 36
20151029 14 768 5 35
20151030 6 769 6 39
20151031 10 770 0 37
If you have week hierarchy or some properties (not to calculate day numbers), it would be easier.
UPDATE (weekly attribute is present):
Here is script for weeks, but please create weekday->day
hierarchy first, e.g:
All
+1
-2015/01/01
-2015/01/08
...
+2
-2015/01/02
-2015/01/09
...
Code has code tricks I'll explain further:
with
member [Measures].[Week Day INFO] as [Report Date].[Week Day].Properties( "Report Date Week Day" )
member [Measures].[Last4Measure] as
/* if there are no empty 4 weeks for the first dates with data, take smaller size */
iif([Report Date].[Week Day].CurrentMember.Lag(3).Parent.Member_Key <> [Report Date].[Week Day].CurrentMember.Parent.Member_Key
,iif([Report Date].[Week Day].CurrentMember.Lag(2).Parent.Member_Key <> [Report Date].[Week Day].CurrentMember.Parent.Member_Key
,iif([Report Date].[Week Day].CurrentMember.Lag(1).Parent.Member_Key <> [Report Date].[Week Day].CurrentMember.Parent.Member_Key
,sum({[Report Date].[Week Day].CurrentMember},[Measures].[Count])
,sum({[Report Date].[Week Day].CurrentMember.Lag(1):[Report Date].[Week Day].CurrentMember},[Measures].[Count]))
,sum({[Report Date].[Week Day].CurrentMember.Lag(2):[Report Date].[Week Day].CurrentMember},[Measures].[Count]))
/* end of fixing, which could be necessary */
/* calculation part */
,sum({[Report Date].[Week Day].CurrentMember.Lag(3):[Report Date].[Week Day].CurrentMember},[Measures].[Count]))
select
{[Measures].[Count],[Measures].[Week Day INFO],[Measures].[Last4Measure]} on 0
,[Report Date].[Report Date].[Day].members on 1
from [DATA]
Week Day INFO
measure is for result viewing only, not necessary in calculations.
Exact code to calculate is really simple: sum({[Report Date].[Week Day].CurrentMember.Lag(3):[Report Date].[Week Day].CurrentMember},[Measures].[Count]))
But it's possible, that you don't have empty or senseless days, which is necessary for this calculation! Since it uses .lag(3)
, and once we try to calculate for the VERY first, second or third weeks, it will take last members from the previous weekday, e.g. to calculate .lag(3) for the Second Wednesday ever (at the beginning of your Date dimension), it will take 2nd Wed, 1st Wed, Last Tue, Pre-last Tue, which is unacceptable, so I've added decreasing lag-level by checking Parent name (since Parent is WeekDay number from the hierarchy we've already created).
Sure thing, you'll use AVG instead of SUM. I used SUM to simplify checking an answer. It's dynamical, not for the very last member only. It depends on current member.
Upvotes: 3