MDX selecting a list of years from Date dimension - ssas

So I have a Date dimension and one of the members is the 4 digit year, eg 2012
Im still pretty new with MDX.
Im looking at an example using AdventureWorks 2008 DW. The following will get the value for 2008, but I want a list of values for ALL the year members.
WITH MEMBER Measures.ValueColumn as [Date].[Calendar].[July 1, 2008].MemberValue
MEMBER Measures.KeyColumn as [Date].[Calendar].[July 1, 2008].Member_Key
MEMBER Measures.NameColumn as [Date].[Calendar].[July 1, 2008].Member_Name
SELECT {Measures.ValueColumn, Measures.KeyColumn, Measures.NameColumn} ON 0
from [Adventure Works]
Results:
ValueColumn KeyColumn NameColumn
7/1/2008 20080701 July 1, 2008
What would I change to get a list of year values for the current year and previous 5 ?
This gives me a list of all the Calendar Year members in the Adventure Works cube
SELECT NULL ON COLUMNS,
[Date].[Calendar Year].MEMBERS ON ROWS
FROM [Adventure Works];
results:
All Periods
CY 2005
CY 2006
CY 2007
CY 2008
CY 2010
Those are the member names, but i want their values

You can use the currentmember function to help combine the two scripts you've mentioned:
WITH
MEMBER [Measures].[ValueColumn]AS
[Date].[Calendar Year].CURRENTMEMBER.MemberValue
MEMBER [Measures].[KeyColumn] AS
[Date].[Calendar Year].CURRENTMEMBER.Member_Key
MEMBER [Measures].[NameColumn] AS
[Date].[Calendar Year].CURRENTMEMBER.Member_Name
SELECT
{
[Measures].[ValueColumn]
, [Measures].[KeyColumn]
, [Measures].[NameColumn]
} ON 0
[Date].[Calendar Year].MEMBERS ON 1
FROM [Adventure Works];
If you want to avoid the All member then add in the level to the expression [Date].[Calendar Year].MEMBERS ON 1 like this:
WITH
MEMBER [Measures].[ValueColumn]AS
[Date].[Calendar Year].CURRENTMEMBER.MemberValue
MEMBER [Measures].[KeyColumn] AS
[Date].[Calendar Year].CURRENTMEMBER.Member_Key
MEMBER [Measures].[NameColumn] AS
[Date].[Calendar Year].CURRENTMEMBER.Member_Name
SELECT
{
[Measures].[ValueColumn]
, [Measures].[KeyColumn]
, [Measures].[NameColumn]
} ON 0
[Date].[Calendar Year].[Calendar Year].MEMBERS ON 1
FROM [Adventure Works];

Related

The Measures hierarchy already appears in the Axis0 axis

I want to use the measures fields in both rows and columns in the MDX query and I have used the below query
SELECT{[Measures].[Average Rate]} ON COLUMNS,
NONEMPTY(
(
TOPCOUNT(
(
Order(
(
([Measures].[End of Day Rate])
),
[Measures].[Average Rate],
ASC
)
),
201
)
),
{[Measures].[End of Day Rate]}
) ON ROWS
FROM
[Adventure Works] CELL PROPERTIES VALUE,
FORMATTED_VALUE,
FORMAT_STRING
but when am using getting the below error.
The Measures hierarchy already appears in the Axis0 axis.
Please share the steps to resolve the error.

How to construct date range query when not all dates exist in hierarchy?

New to MDX and inherited an application using the following to retrieve the last 7 work days.
Note, the actual dates are generated dynamically.
WHERE ( [DimCalendar].[WorkDayHierarchy].[WorkDate].&[2016-03-25T00:00:00]
: [DimCalendar].[WorkDayHierarchy].[WorkDate].&[2016-03-26T00:00:00].lag(6)
)
I expected it to use the previous 7 work days:
2016-03-17 to 2016-03-25
But instead, it uses future dates
2016-03-25 to latest date in DimCalendar
From what I've read, it's because 2016-03-26 doesn't exist in the hierarchy, so the end range becomes NULL, which explains the future dates...
[WorkDate].&[2016-03-25T00:00:00] : NULL
The problem is the date values are generated dynamically, and I don't know in advance which values exist in the hierarchy. I'm not sure how to construct the MDX date range to get the desired results.
I've tried using <= and FILTER but keep getting conversion errors. With plain SQL this would be easy. I could just write:
WHERE [WorkDate] >= '2016-03-17'
AND [WorkDate] <= '2016-03-25'
Any ideas what the equivalent filter would be in MDX?
A quick fix could be
WHERE ( [DimCalendar].[WorkDayHierarchy].[WorkDate].&[2016-03-25T00:00:00].lag(7)
: [DimCalendar].[WorkDayHierarchy].[WorkDate].&[2016-03-25T00:00:00]
)
But this will only work if the past date is in the hierarchy, which in this case in 2016-03-25.
Edit: Based on the issue below
///Query without using strong names . (no &)
select {[Measures].[Internet Order Count] }
on columns,
[Date].[Day of Year].[1]:[Date].[Day of Year].[10]
on rows
from [Adventure Works]
//This query filters by making the dimension member value, as a measure value.
WITH
MEMBER [Measures].[Data Type] AS
[Date].[Day of Year].CurrentMember.Properties ("Member_Value",TYPED)
select {[Measures].[Internet Order Count] }
on columns,
filter ([Date].[Day of Year].[Day of Year],[Measures].[Data Type]<12)
on rows
from [Adventure Works]
//You can also try the below one
select
{[Measures].[Internet Sales Amount],[Measures].[Reseller Sales Amount]}
on columns,
filter([Date].[Day of Year].[Day of Year],
[Date].[Day of Year].currentmember.Properties ("Member_Value",TYPED)
>12 and [Date].[Day of Year].currentmember.Properties ("Member_Value",TYPED)<20)
on rows
from
[Adventure Works]
Edit
//this might be the exact solution that would work for you
select
{[Measures].[Internet Sales Amount],[Measures].[Reseller Sales Amount]}
on columns,
([Geography].[Country].&[United States]
)
on rows
from
[Adventure Works]
where
filter([Date].[Day of Year].[Day of Year],
[Date].[Day of Year].currentmember.Properties ("Member_Value",TYPED)
>12 and [Date].[Day of Year].currentmember.Properties ("Member_Value",TYPED)<20)

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