Adjust TOPCOUNT to be time aware - mdx

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] )

Related

How do I build a MDX query that considers only facts that happened in the last 10 days of February?

I have a fact table that has a time dimension, which contains year, month, day and hour.
I was able to find ways to filter things that happened in a given day, or month (simple where/filter by the desired level). But I would like to create an MDX query that filter the results so my cube has information about the facts recorded in the last 10 days of febraury.
Is there anyway I can do it?
Assuming you have all the days of February in your cube, you could use a set inside there WHERE clause.
Something like this..
WHERE ([Date].Month)
Supposing you have a Year-Month-Day-Hour hierarchy in place and there may be some dates missing
Select....... on COLUMN,
....... ON ROWS
FROM ....
WHERE
({[Time].[Month].&[Feb 2015].LastChild.LAG(10) : [Date].[Month].&[Feb 2015].LastChild})
If no dates are missing in the date dim,
select ... ON COLUMNS,
... ON ROWS
FROM ...
WHERE
({[Time].[Date].&[02/19/2015] : [Date].[Date].&[02/28/2015]})
If you want the sales for last 10 days of Feb for every year:
SELECT Measures.Sales ON COLUMNS,
Products.Products.MEMBERS ON ROWS
FROM
(
SELECT
generate //This would build the set for the last 10 days of Feb for every year
(
[Time].[Year].[All].children,
TAIL //This returns the last 10 days of february(second month)
(
[Time].[Year].CURRENTMEMBER.FIRSTCHILD.LEAD(1).CHILDREN,
10
)
) ON COLUMNS
FROM YourCube
)
Just as some extra info - if you want a "rolling" 10 day sum or 10 day average then code similar to the following is a possible approach:
WITH
MEMBER [Measures].[Sum 10] AS
Sum
(
LastPeriods
(10
,[Date].[Calendar].CurrentMember
)
,[Measures].[Internet Order Count]
)
MEMBER [Measures].[MovAvg 10] AS
Avg
(
LastPeriods
(10
,[Date].[Date].CurrentMember
)
,[Measures].[Internet Order Count]
), format_string = "#.000"
SELECT
{
[Measures].[Internet Order Count]
,[Measures].[Sum 10]
,[Measures].[MovAvg 10]
} ON 0
,Descendants
(
[Date].[Calendar].[Month].&[2006]&[2]
,[Date].[Calendar].[Date]
) ON 1
FROM [Adventure Works];
It returns data like the following:

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];

How to get data from last days

I'm a newcomer to SQL MDX and don't know exactly how to achieve this.
I need to get data from my cube for the last X days from the last available data.
The following is my code:
SELECT { [Measures].[Fact Stays Count], [Measures].[Time Spent] } ON COLUMNS,
NON EMPTY { ( [Dim Locals].[Local Description].[Local Description].ALLMEMBERS * [FK Date].[Date].[Date] ) } ON ROWS
FROM
(
select { TAIL(FILTER([FK Date].[Date].MEMBERS, NOT ISEMPTY([FK Date].[Date].CURRENTMEMBER)),30) } ON COLUMNS
FROM (
SELECT ( STRTOSET(#userId, CONSTRAINED) ) ON COLUMNS
FROM [DW]
)
)
The problem is the query returns the last 30 days where data exists, not the last 30 consecutive calendar days.
How can I change the query to get the results I want?
Try this. The only thing I changed is the select with the dates in it. Instead of asking for the last 30 days where there is data for the measures, I'm asking for the last day where there is data for the measures, getting that last item and then doing the lag of 29 days for the beginning of the date range and then without the lag (to the last day with data) for the end of the date range.
SELECT { [Measures].[Fact Stays Count], [Measures].[Time Spent] } ON COLUMNS,
NON EMPTY { ( [Dim Locals].[Local Description].[Local Description].ALLMEMBERS * [FK Date].[Date].[Date] ) } ON ROWS
FROM
(
select { TAIL(FILTER([FK Date].[Date].MEMBERS, NOT ISEMPTY([FK Date].[Date].CURRENTMEMBER)),1).item(0).lag(29): TAIL(FILTER([FK Date].[Date].MEMBERS, NOT ISEMPTY([FK Date].[Date].CURRENTMEMBER)),1).itm(0)} ON COLUMNS
FROM (
SELECT ( STRTOSET(#userId, CONSTRAINED) ) ON COLUMNS
FROM [DW]
)
)
Be aware that the way you have the query now will return the last day where there is data for both measures. If those two measures don't line up it might not provide what you want. For instance, if there is data through Dec 30 2013 on Fact Stays Count and data through Jan 5 2014 on Time Spent, it would return Dec 30 2013. If you want it to depend on both measures, you are good. If you want it to depend on one measures, you can switch it to be something like the below instead.
Tail(Filter([FK Date].[Date].[Date].MEMBERS.MEMBERS, [Measures].[Fact Stays Count] >0),1).item(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.

Check if level in any dimension hierarchy is [Foo]

In my Date Dimension I have 2 hierarchies:
[Year - Week - Date] and [Year - Month - Date]
I want to check in a Cube Calculation if the current level of the Date Dimension is [Date] (the lowest level) or something higher.
How can I achieve this?
Background: I need this to calculate how many working days there were in a period, for an employee.
I currently have this code (untested) that should do the trick for 1 hierarchy, but I guess this will fail when users are using the [Year - Week - Date] hierarchy.
CASE WHEN
[Date].[Year - Month - Date].CURRENTMEMBER.Level
IS
[Date].[Year - Month - Date].[Date]
THEN
//at day level,
//if there is any duration booked, this is a working day
IIF([Measures].[Duration] = 0,0,1)
ELSE
//at higher than day level,
//count days
COUNT(
// where duration > 0 (employee work day)
FILTER(
Descendants([Date].[Year - Month - Date].CURRENTMEMBER, [Date].[Year - Month - Date].[Date]),
[Measures].[Duration] > 0
)
)
END
tl;dr how do I make the above code also work for [Year - Week - Date] hierarchy in the cleanest way possible.
Let's assume the hierarchy (aka attribute) [Date].[Date] exists. If this is the case you can simplify :
COUNT(
FILTER( Existing [Date].[Date].members, [Measures].[Duration] > 0 )
)
The Existing will force to apply an autoexists on the [Date] dimension. This is mainly a performance improvement as it's avoid to evaluate (fact-vise) all tuples.
Once the former examples is clear, we can merge it with your version for the fastest solution (no need for the first iif) :
COUNT(
FILTER( Existing
Descendants([Date].[Year - Month - Date].CURRENTMEMBER, [Date].[Year - Month - Date].[Date],self)
, [Measures].[Duration] > 0 )
)
Further improvements may be possible adding a new measure with a different aggregation type or adding an iif to change which of the two hierarchies is used for descendants (e.g. the one where the currentmember and the defaulmember is not equal)
If you are looking for the cleanest way to calculate how many working days there were in a period, you have to emulate the behavior of regular measure. Otherwise you will get error or bad data in the case of a multiselect on [Date]. In addition, it is desirable to get rid of the Count(Filter(...)) expression to keep the block computation mode (see Optimizing Count(Filter(...)) expressions in MDX). To do this, follow these steps:
Go to the data source view.
Create a new named calculation next to the [Duration] column (in the same fact table).
Column name is "Working days", expression is "null".
Create a new regular measure based on "Working days" column. Aggregation function is Sum.
Write in the MDX script:
(
[Date].[Date].[Date].Members,
[Measures].[Working days]
) = Iif( [Measures].[Duration] > 0, 1, null );
IsLeaf() will tell you if a member is at the bottom level.
Look at SCOPE commands in your calculation script, I do something similar for my YMD calender and my YWD calendar:
CREATE MEMBER CurrentCube.Measures.Example AS NULL //Dummy will be calc'd later
SCOPE (DESCENDANTS([Date].[Calender Y M D],,AFTER));
Measures.Example = 1; //Fill in for monthly calcs
END SCOPE
SCOPE (DESCENDANTS([Date].[Calender Y W D],,AFTER));
Measures.Example = 2; //Fill in for weekly calcs
END SCOPE
The syntax with the ,,AFTER is to exclude the All member if I remember rightly, I'm trying to dif the link up but can't find it. Alternatively, if the calculation works well with the ALL member, just use SCOPE([Date].[Calender Y W D])