MDX to normalize set of measures - mdx

I am trying to create a dataset for an SSRS report as documented here:
http://sqlblog.com/blogs/stacia_misner/archive/2010/10/08/29249.aspx
The challenge is that I have multiple measures who's data I want to include in the measure column and I want to include the name of the measure in the RowValue column. So where the following query returns only data for measure "Sales Amount":
with
member [Measures].[Measure] as [Measures].[Sales Amount]
member [Measures].[RowValue] as [Product].[Category].CurrentMember.Name
member [Measures].[ColumnValue] as [Date].[Calendar Year].CurrentMember.Name
select {[Measures].[Measure], [Measures].[RowValue], [Measures].[ColumnValue]} on columns,
non empty ([Product].[Category].[Category].Members, [Date].[Calendar Year].[Calendar Year].Members) on rows
from [Adventure Works]
What I want to do is run the following type of query but have the data returned in the structure of the query above which would allow me to plug it into an SSRS report matrix:
WITH
MEMBER measures.SalesAmount AS [Measures].[Sales Amount]
MEMBER measures.CustomerCount AS [Measures].[Customer Count]
MEMBER measures.InternetFreightCost AS [Measures].[Internet Freight Cost]
SELECT [Date].[Calendar Year].[Calendar Year].Members ON COLUMNS,
{measures.SalesAmount,measures.CustomerCount,measures.InternetFreightCost} ON ROWS
FROM [Adventure Works]
Do any of the MDX ninjas know if this is even possible with MDX?

with member [Geography].[City].[Sales Amount] as 1
member [Geography].[City].[Customer Count] as 1
member [Geography].[City].[Freight Cost] as 1
member [Measures].[RowValue] as [Geography].[City].CurrentMember.Name
member [Measures].[ColumnValue] as [Date].[Calendar Year].CurrentMember.Name
member [Measures].[Measure] as
CASE
WHEN [Geography].[City].CurrentMember IS [Geography].[City].[Sales Amount]
THEN ([Measures].[Internet Sales Amount], [Geography].[City].[All Geographies])
WHEN [Geography].[City].CurrentMember IS [Geography].[City].[Customer Count]
THEN ([Measures].[Customer Count], [Geography].[City].[All Geographies])
WHEN [Geography].[City].CurrentMember IS [Geography].[City].[Freight Cost]
THEN ([Measures].[Internet Freight Cost], [Geography].[City].[All Geographies])
END
select {[Measures].[RowValue], [Measures].[ColumnValue], [Measures].[Measure]}
on columns,
{ [Geography].[City].[Sales Amount], [Geography].[City].[Customer Count], [Geography].[City].[Freight Cost]}
*
[Date].[Calendar Year].[Calendar Year].Members
having [Measures].[Measure] <> null
on rows
from [Adventure Works]
should deliver what you want. I used [Geography].[City] as an utility hierarchy. This can be any hierarchy unused in the query. I chose this one, as it is unrelated to both measure groups used in the query, and hence very unlikely to be used in any query. Some Cube designers create one or two one-member dummy dimensions in their cubes that are unrelated to any measure group, and can be used just like here in order to create calculated members on them.
One of the difficulties with ReportingServices queries is that measures must always be in the columns, and no other hierarchy may be in the columns. Hence, if we want to have the measures in the rows, we must move them to another hierarchy. This is done in two steps: First, we create dummy members on the utility hierarchy, and then map these to the measure needed in the CASE construct of the [Measures].[Measure] definition, where we need to use the default member of the utility dimension (in most cases the All member) in order to get something different than the 1 that I used for the dummy value.
Finally: non empty does not work properly with this construct, as [Measures].[RowValue] and [Measures].[ColumnValue] are never null. Hence I replaced it by HAVING, which can look at specific column values within the row.

Related

Create Calculate Measures in SSAS

Please consider this scenarios:
I have a cube with one Fact table and one measure called SalesAmount . Now I want to create a measure based on these 2 Selects:
Select 1:
Select [Measures].[SalesAmount]
From MyCube
Where [Product].[Color].[Gray]
and Select 2:
Select [Measures].[SalesAmount]
From MyCube
Where [Dates].[Calendar Year].&[2015]
The problem is in Add Calculate Member there is a box for simple formula. How can I create a measure for Select 1 + Select 2?
Thanks
I am not sure where are you looking at this Add Calculate Member option, but you could try something like this (Adventure Works database).
with member measures.[MyMeasure1]
as
([Measures].[Internet Sales Amount], [Date].[Calendar].[Calendar Year].&[2006])
member measures.[MyMeasure2]
as
([Measures].[Internet Sales Amount], [Product].[Color].&[Grey])
member measures.[MyMeasure12]
as
measures.[MyMeasure1] + measures.[MyMeasure2]
select {[Measures].[Internet Sales Amount], measures.[MyMeasure1], measures.[MyMeasure2] , measures.[MyMeasure12]} on 0
from [Adventure Works]
You can also create those members from Visual Studio Data Tools - in Calculations tab:

AdventureWorks date dimension shows different results depending on selected hierarchy

Here are two simple queries which shows data on month level filtered by dates.
In the first query I am using Month level of "Date.Calendar" user hierarchy.
SELECT
NON EMPTY { [Measures].[Internet Sales Amount] } ON 0,
NON EMPTY { [Date].[Calendar].[Month].&[2013]&[1] } ON 1
FROM [Adventure Works]
WHERE {[Date].[Date].&[20130105]:[Date].[Date].&[20130106]}
And recieved - January 2013 -> $857,689.91
Results
In the second query I am using "Date.Month of Year" attribute hierarchy.
SELECT
NON EMPTY { [Measures].[Internet Sales Amount]} ON 0,
NON EMPTY { [Date].[Month of Year].&[1] } ON 1
FROM [Adventure Works]
WHERE { [Date].[Date].&[20130105] : [Date].[Date].&[20130106] }
And received - January -> $54,468.46
Results
I can not figure out why these two queries show different results. If the same dimension is used and data are filtered/sliced on the lovest possible level.
Here are values for each of these dates.
SELECT
NON EMPTY { [Measures].[Internet Sales Amount]} ON 0,
NON EMPTY { [Date].[Calendar].[Date] } ON 1
FROM [Adventure Works]
WHERE { [Date].[Date].&[20130105] : [Date].[Date].&[20130106] }
January 5, 2013 $32,681.44
January 6, 2013 $21,787.02
Result
Total value for these two dates is equal with the second querie's result - $54,468.46
I understand that in the first query it is user hierarchy and the second query it is attribute hierarchy from the Date dimension but I can not figure out which rule(s) tells to calculate these values differently.
If someone could explain this logic behind - it would be very helpful. Any link to some resource which explains this logic also could help.
BTW: I have created simple cube with simple Date dimension which consists just of attribute hierrarchies (date, month, year) and it still works like in the first query so it is not clear why it behaves like that.
I explored the reason for this behavior, and I think I have figured out the reason. The explanation below is based on the book SQL SERVER 2008 MDX Step by Step pages 51-58(especially Avoiding Reference Conflicts).
Your problem is a typical Reference Conflict problem.In MDX a hierarchy cannot be used more than once in a given tuple, but if you are using a USER Hierarchy and its under lying Attribute Hierarchy, you essentially by-pass this check. This is what happened in your query
In your first query you are using the User Hierarchy
[Date].[Calendar].[Month].&[2013]&1
In MDX a User Hierarchy is translated to Attribute Hierarchies. In your first query
SELECT
NON EMPTY { [Measures].[Internet Sales Amount] } ON 0,
NON EMPTY { [Date].[Calendar].[Month].&[2013]&1 } ON 1
FROM [Adventure Works] WHERE
{[Date].[Date].&[20130105]:[Date].[Date].&[20130106]}
you are using a User Hierarchy "[Date].[Calendar].[Month].&[2013]&1", which in its last level has "[Date].[Date]". Then in the where clause you use the same "[Date].[Date]" Attribute Hierarchy to filter. Since in the USER Hierarchy you have not used the leaf level, hence you have made a partial address, therefore the members and its ancestors are resolved. All the descendants are ignored in translation. Take a look at the below query(This is based on your first query,I have purposely removed your where clause).
with member [Measures].[CalendarYear] as [Date].[Calendar Year].currentmember.name
member [Measures].[CalendarSemester] as [Date].[Calendar Semester of Year].currentmember.name
member [Measures].[CalendarQuater] as [Date].[Calendar Quarter of Year].currentmember.name
member [Measures].[CalendarMonth] as [Date].[Month of Year].currentmember.name
member [Measures].[CalendarDate] as [Date].[Date].currentmember.name
SELECT
NON EMPTY { [Measures].[Internet Sales Amount] ,[Measures].[CalendarYear],[Measures].[CalendarSemester],[Measures].[CalendarQuater],[Measures].[CalendarMonth],[Measures].[CalendarDate]} ON 0,
NON EMPTY { [Date].[Calendar].[Month].&[2013]&[1] } ON 1
FROM [Adventure Works]
Result.
Notice that Calendar year, Semester and quarter all are showing non-default values. But we never used them. This shows that translation of User Hierarchy is done into underlying Attribute Hierarchies. Now take a look at Calendar, it is still showing "All Period". Since it was ignored.
Now if you add your where clause back, the Date still shows "All Period", there are two reasons
1) Because it was ignored in User Hierarchy translation ,
2)You used a range in where. If you replace your row axis tuple with your where tuple it will still show "All Period" as a range is based. However while resolving it will take just two dates.
Based on this while resolving your query, it had two translation for Date attribute hierarchy, one said to ignore it based on User Hierarchy, the other provided a range. This is where due to conflict the result is in-correct.
Now lets consider the query you gave me in your comment earlier
with member [Measures].[CalendarYear] as [Date].[Calendar Year].currentmember.name
member [Measures].[CalendarSemester] as [Date].[Calendar Semester of Year].currentmember.name
member [Measures].[CalendarQuater] as [Date].[Calendar Quarter of Year].currentmember.name
member [Measures].[CalendarMonth] as [Date].[Month of Year].currentmember.name
member [Measures].[CalendarDate] as [Date].[Date].currentmember.name
SELECT
NON EMPTY { [Measures].[Internet Sales Amount] ,[Measures].[CalendarYear],[Measures].[CalendarSemester],[Measures].[CalendarQuater],[Measures].[CalendarMonth],[Measures].[CalendarDate]} ON 0,
NON EMPTY { [Date].[Calendar].[Month].&[2013]&[1] } ON 1
FROM [Adventure Works]
WHERE {[Date].[Date].&[20130105]}
Result:
Notice that this time the resolution of a single member, was used instead of the User hierarchy resolution. Now this behavior might be due to the fact that one translation is giving "All Period" and the next is giving a member, hence the member won.
To further confirm this, I made a change to my AdventureWorks sample. The Date attribute hierarchy is based on "Simple Date" column. I exposed "Simple Date" as a separate attribute and processed my cube.
Take a look at the "Simple Date" query and results.
with member [Measures].[CalendarYear] as [Date].[Calendar Year].currentmember.name
member [Measures].[CalendarSemester] as [Date].[Calendar Semester of Year].currentmember.name
member [Measures].[CalendarQuater] as [Date].[Calendar Quarter of Year].currentmember.name
member [Measures].[CalendarMonth] as [Date].[Month of Year].currentmember.name
member [Measures].[CalendarDate] as [Date].[Date].currentmember.name
SELECT
NON EMPTY { [Measures].[Internet Sales Amount] ,[Measures].[CalendarYear],[Measures].[CalendarSemester],[Measures].[CalendarQuater],[Measures].[CalendarMonth],[Measures].[CalendarDate]} ON 0,
NON EMPTY { [Date].[Calendar].[Month].&[2013]&[1] } ON 1
FROM [Adventure Works]
WHERE
--{[Date].[Date].&[20130105]}
{[Date].[Simple Date].&[20130105]:[Date].[Simple Date].&[20130106]}
Results:

Write calculated member equivalent to scope assignment

I am trying to better understand scopes and calculated members better, so in the AdventureWorks database I did the following:
I wrote this simple scope statement:
SCOPE([Customer].[Customer].[All Customers], [Measures].[Average Rate]);
/* This expression sets the value of the Amount measure */
THIS = 999;
END SCOPE;
And equivalent calculated member will be this:
Create Member CurrentCube.[Measures].[My Measure]
AS
iif([Customer].[Customer].currentmember IS [Customer].[Customer].[All Customers], 999, [Measures].[Average Rate]);
But I am not sure how to create calculated member that will be equivalent to this scope assignment:
SCOPE([Customer].[Country].members, [Measures].[Average Rate]);
/* This expression sets the value of the Amount measure */
THIS = 999;
END SCOPE;
Actually I am not sure how to write iff that will check if current member of Customer dimension is a member of [Customer].[Country]
Here you go
///Will Display 999 for any member apart from All
with Member [Measures].[My Measure]
AS
iif([Customer].[Country].currentmember.Properties ("Member_Value",TYPED)='All Customers',[Measures].[Internet Sales Amount] , 999)
select {[Measures].[Internet Sales Amount],[Measures].[My Measure]}
on columns,
[Customer].[Country].members
on rows
from [Adventure Works]
///Will display 999 for any member
with Member [Measures].[My Measure]
AS
iif([Customer].[Country].currentmember.Properties ("Member_Value",TYPED)='WhatEver', 999, 999)
select {[Measures].[Internet Sales Amount],[Measures].[My Measure]}
on columns,
[Customer].[Country].members
on rows
from [Adventure Works]
//Below queries help to check if a member is part of a hierarchy
///////////////////////////////////////////////////////////////////////////
//Query checks if a Member is part of a hierarchy
with MEMBER TestIFAValidMember
as
[Product].[Subcategory].[InvalidMember].UniqueName
MEMBER TestIFAValidMember2
as
[Product].[Subcategory].[Caps].UniqueName
select {TestIFAValidMember,TestIFAValidMember2}
on columns
FROM [Adventure Works]
You can also try
WITH MEMBER MEASURES.NotMember AS
IsSibling([Product].[Category].CURRENTMEMBER, [Product].[Category].[Invalid])
MEMBER MEASURES.ISMember AS
IsSibling([Product].[Category].CURRENTMEMBER, [Product].[Category].[Bikes])
SELECT {MEASURES.NotMember,MEASURES.ISMember} ON 0,
[Product].[Category].MEMBERS ON 1
FROM [Adventure Works]

Non-empty previous value - MDX

I am using Performance Point Dashboard Designer 2013 and SharePoint Server 2013 for building dashboards. I am using SSAS2012 for Cube.
I have a scenario similar to the one illustrated by figure below. I am required to find Previous Non-Empty value for purpose of finding Trends.
Measure: [Quota]
Dimension: [Date].[Calendar Date].[Date]
The script ([Measures].[Quota], [Date].[Calendar Date].PrevMember) gives you a previous date. Lets say for date 27-Jan-13 whose Quota value is 87, it returns 26-Jan-13 which has null value. I want it to return 21-Jan-13 that has some Quota value. And for date 21-Jan-13, I want to return 15-Jan-13.
I wonder if this is possible.
Thanks,
Merin
After long searches and hits & trials and so on, I think I invented a solution of my own for myself.
Following is the script for my Calculated Member.
(
[Quota],
Tail
(
Nonempty
( LastPeriods(15, [Date].[Calendar Date].PrevMember)
,[Quota]
)
).Item(0)
)
Explanation
The number 15 means it will look for non-empty measures up to 15 siblings.
Now we know up to how many siblings to traverse back, in this case 15.
Lets find 15 previous siblings (both empty and non-empty) excluding current member.
(LastPeriods(15, [Date].[Calendar Date].PrevMember)
Since it will yield both empty and non-empty members, lets filter out empty members in terms of measure [Quota]. If we don't specify measure here, it will use default measure whatever it is and we may not get desired result.
Nonempty(LastPeriods(15, [Date].[Calendar Date].PrevMember),[Quota])
We may have several members in the output. And we will choose the last one.
Tail
(
Nonempty
( LastPeriods(15, [Date].[Calendar Date].PrevMember)
,[Quota]
)
)
So far, the script above gives previous non-empty member. Now we want to implement this member for our measure [Quota].
Hence we get the script below ready to create a Calculated Member.
(
[Quota],
Tail
(
Nonempty
( LastPeriods(15, [Date].[Calendar Date].PrevMember)
,[Quota]
)
).Item(0)
)
You can use recursion to define this.
The following query delivers something similar for the Adventure Works cube:
WITH member [Measures].[Prev non empty] AS
IIf(IsEmpty(([Date].[Calendar].CurrentMember.PrevMember, [Measures].[Internet Sales Amount])),
([Date].[Calendar].CurrentMember.PrevMember, [Measures].[Prev non empty]),
([Date].[Calendar].CurrentMember.PrevMember, [Measures].[Internet Sales Amount])
), format_String = '$#,##0.00'
SELECT {[Measures].[Internet Sales Amount], [Measures].[Prev non empty]}
ON COLUMNS,
non empty
Descendants([Date].[Calendar].[Month].&[2007]&[12], [Date].[Calendar].[Date])
ON ROWS
FROM [Adventure Works]
WHERE [Customer].[Customer].&[12650]
You would have to replace the name of the date hierarchy, as well as the measure name from Internet Sales Amount to Quota in the recursive definition of the measure Prev non empty.

MDX Scope Vs WIth member

Using the Adventure works Cube, if I run the following code (from MS example):
with
Member [Measures].[Internet Sales Amount - Range]
AS aggregate ( [Date].[Fiscal].[Date].&[20080430]:[Date].[Fiscal].[Date].&[20080502] , [Measures].[Internet Sales Amount] )
SELECT
{ [Measures].[Internet Sales Amount - Range] } ON COLUMNS,
{ [Product].[Category].Members } ON ROWS
FROM
[Adventure Works]
You get the proper results.
However, when I implement similar functionality via the SCOPE command in a calculated tab:
scope ([Product].[Category], [Measures].[Internet Sales Amount]);
this = aggregate ( [Date].[Fiscal].[Date].&[20080430]:[Date].[Fiscal].[Date].&[20080502] , [Measures].[Internet Sales Amount] );
end scope;
and then run the following mdx query:
SELECT
{ [Measures].[Internet Sales Amount] } ON COLUMNS,
{ [Product].[Category].Members } ON ROWS
FROM
[Adventure Works]
I get vastly different results. The scope appears to be ignoring the date range or ignoring the category members, i'm not sure what. I am having a similar issue on my cube when creating a new scope with a date range.
Anyone know what's going on with the scope?
Use
CREATE MEMBER CurrentCube.[Measures].[Internet Sales Amount - Range2] AS NULL;
scope ([Measures].[Internet Sales Amount - Range2]);
this = aggregate ( [Date].[Fiscal].[Date].&[20080430]:[Date].[Fiscal].[Date].&[20080502] , [Measures].[Internet Sales Amount] );
end scope;
Otherwise, there might be an infinite recursion as you reference the measure inside the scope which you use in the SCOPE itself as well.