Measure lacking context - ssas

I am struggling to force the measure CountTopPromos to be contextual.
Currently it is calculating a count of all the tuples in TopX. Against the version of AdWrks that I am running this is 14.
How do I force a context, of the current year, onto the measure CountTopPromos
WITH
SET FullSet AS
NonEmpty
(
[Date].[Calendar].[Calendar Year].MEMBERS
*
[Promotion].[Promotion].MEMBERS
,[Measures].[Reseller Order Count]
)
SET TopX AS
Generate
(
[Date].[Calendar].[Calendar Year].MEMBERS
,TopPercent
(
[Date].[Calendar].CurrentMember
*
Extract
(
FullSet
,[Promotion].[Promotion]
)
,90
,[Measures].[Reseller Order Count]
)
)
MEMBER [Measures].[CountTopPromos] AS
Count(TopX) //<<<<<<<<<<<< CONTEXT LACKING HERE <<<<<<<
SELECT
NON EMPTY
{[Measures].[CountTopPromos]} ON 0
,{TopX} ON 1
FROM [Adventure Works];
Currently it returns the following:
I'd like CountTopPromos to be in the context of the year so the desired output is as follows:

As sets are evaluated before the axes are defined (see my answer to this question), you cannot get row context into the calculation if you use the named set TopX. You could, however, copy its definition into the definition of [Measures].[CountTopPromos], replacing [Date].[Calendar].[Calendar Year].MEMBERS with [Date].[Calendar].CurrentMember:
MEMBER [Measures].[CountTopPromos] AS
Count(
Generate
(
[Date].[Calendar].currentMember
,TopPercent
(
[Date].[Calendar].CurrentMember
*
Extract
(
FullSet
,[Promotion].[Promotion]
)
,90
,[Measures].[Reseller Order Count]
)
)
)
delivers the result you want. And - as the Generate would loop over just one member here - you could omit that as well:
MEMBER [Measures].[CountTopPromos] AS
Count(
TopPercent
(
[Date].[Calendar].CurrentMember
*
Extract
(
FullSet
,[Promotion].[Promotion]
)
,90
,[Measures].[Reseller Order Count]
)
)
and will get the same result.

Another alternative that is quite pretty:
MEMBER [Measures].[CountTopPromos] AS
Count
(
Intersect
(
{
[Date].[Calendar].CurrentMember * [Promotion].[Promotion].MEMBERS
}
,TopX
)
)

Related

Using OR condition in MDX with different hierarchies of same dimension

I would like to get data from cube where [Product].[Product Level2].&[S1050]&[S1050.080] OR [Product].[Product Level4].&[S1060.012.701003]. So combine two hierarchies from same measure in set.
SELECT NON EMPTY {
[Measures].[Product Sales], [Measures].[Sales Quantity]
} ON COLUMNS,
NON EMPTY { (
[Product].[EAN CODE].[EAN CODE].ALLMEMBERS
* [Product].[Product].[Product].ALLMEMBERS
* [Date].[Calender Week].[Calender Week].ALLMEMBERS
* [Store].[Store Code].[Store Code].ALLMEMBERS
* [Store].[Store].[Store].ALLMEMBERS
) } ON ROWS
FROM (
SELECT ( -{ [Store].[Store Code].&[0032], [Store].[Store Code].&[0033] } ) ON COLUMNS FROM (
SELECT ( { [Date].[Calender Week].&[2019]&[IW201906] } ) ON COLUMNS FROM [Cube]
)
)
WHERE (
{[Product].[Product Level2].&[S1050]&[S1050.080], [Product].[Product Level4 Code].&[S1060.012.701003]}
)
The above query is returning "Members, tuples or sets must use the same hierarchies in the function."
Thank you for helping
You need to write your where like this
{(
[Product].[Product Level2].&[S1050]&[S1050.080], [Product].[Product Level4 Code].defaultmember),
{[Product].[Product Level2].defaultmember, [Product].[Product Level4 Code].&[S1060.012.701003]}
)

Top X of Top Y with RestOf member where X and Y are hierarchies from different dimensions

This works fine:
WITH
SET [AllCountries] AS [Country].[Country].MEMBERS
SET [AllStates] AS [State-Province].[State-Province].MEMBERS
SET [Top2States] AS
Generate
(
[AllCountries]
,TopCount
(
(EXISTING
[AllStates])
,3
,[Measures].[Internet Order Count]
)
)
MEMBER [State-Province].[All].[RestOfCountry] AS
Aggregate({(EXISTING {[AllStates]} - [Top2States])})
SELECT
{[Measures].[Internet Order Count]} ON COLUMNS
,{
[AllCountries]
*
{
[State-Province].[All]
,[Top2States]
,[State-Province].[All].[RestOfCountry]
}
} ON ROWS
FROM [Adventure Works];
The EXISTING keyword helps a lot .
If the two hierarchies ON ROWS are not from the same dimension, in the way that countries and states are in the above, then we have something like the following:
WITH
SET [AllCountries] AS [Country].[Country].MEMBERS
SET [AllCats] AS [Product].[Category].[Category].MEMBERS
SET [Top5Cats] AS
Generate
(
[AllCountries]
,TopCount
(
(EXISTING
[AllCats])
,5
,[Measures].[Internet Order Count]
)
)
MEMBER [Product].[Category].[All].[RestOfProds] AS
Aggregate({(EXISTING {[AllCats]} - [Top5Cats])})
SELECT
{[Measures].[Internet Order Count]} ON COLUMNS
,{
[AllCountries]
*
{
[Product].[Category].[All]
,[Top5Cats]
,[Product].[Category].[All].[RestOfCountry]
}
} ON ROWS
FROM [Adventure Works];
You can see in the results of the above that the same set of categories are repeated against each country, in the same order i.e. the engine is not finding the topCount per country. EXISTING is now redundant.
How can we adapt the second script above so that it has similar functionality as the top script?
Edit
A better example is the following using Product. It is as if the engine is finding the TopCount for All countries and then putting the same set against each country. I'd like the TopCount for each country:
WITH
SET [AllCountries] AS
[Country].[Country].MEMBERS
SET [AllProds] AS
[Product].[Product].[Product].MEMBERS
SET [Top5Prods] AS
Generate
(
[AllCountries]
,TopCount
(
(EXISTING
[AllProds])
,5
,[Measures].[Internet Order Count]
)
)
MEMBER [Product].[Product].[All].[RestOfProds] AS
Aggregate({(EXISTING {[AllProds]} - [Top5Prods])})
SELECT
{[Measures].[Internet Order Count]} ON COLUMNS
,NON EMPTY
{
[AllCountries]
*
{
[Product].[Product].[All]
,[Top5Prods]
,[Product].[Product].[All].[RestOfProds]
}
} ON ROWS
FROM [Adventure Works];
Edit2
This is the latest version via Sourav's ideas - unfortunately the RestOfProds members are not functioning correctly:
WITH
SET [AllCountries] AS
[Country].[Country].MEMBERS
SET [AllProds] AS
[Product].[Product].[Product].MEMBERS
SET [Top5Prods] AS
Generate
(
[AllCountries] AS a
,
{
(
a.Current
,[Product].[Product].[All]
)
+
//The top x prods
TopCount
(
NonEmpty
(
a.Current * [AllProds]
,[Measures].[Internet Sales Amount]
)
,5
,[Measures].[Internet Sales Amount]
)
}
)
SET [RestOfProds] AS
Extract
(
{[AllCountries] * [AllProds]} - [Top5Prods]
,[Product].[Product]
)
MEMBER [Product].[Product].[All].[RestOfProds] AS
Aggregate([RestOfProds])
SELECT
{[Measures].[Internet Sales Amount]} ON COLUMNS
,{
[Top5Prods]
,
[AllCountries] * [Product].[Product].[All].[RestOfProds]
} ON ROWS
FROM [Adventure Works];
Edit3
The following has the correct order so that the member RestOfProds always follows it's respective top 5
WITH
SET [AllCountries] AS
[Country].[Country].MEMBERS
SET [AllProds] AS
[Product].[Product].[Product].MEMBERS
SET [Top5Prods] AS
Generate
(
[AllCountries] AS a
,{
//The top x prods
TopCount
(
NonEmpty
(
a.Current * [AllProds]
,[Measures].[Internet Sales Amount]
)
,5
,[Measures].[Internet Sales Amount]
)
}
)
MEMBER [Product].[Product].[All].[RestOfProds] AS
Aggregate([Country].CurrentMember * [AllProds] - [Top5Prods])
SELECT
{[Measures].[Internet Sales Amount]} ON COLUMNS
,Generate
(
[AllCountries] AS X
,
Order
(
Intersect
(
X.CurrentMember * [AllProds]
,[Top5Prods]
)
,[Measures].[Internet Sales Amount]
,bdesc
)
+
{X.CurrentMember * {[Product].[Product].[All].[RestOfProds]}}
) ON ROWS
FROM [Adventure Works];
Edit4
The following has the correct order so that the member RestOfProds always follows it's respective top 5 + I've added a further set on rows:
WITH
SET [2months] AS
{
[Date].[Calendar].[Month].&[2007]&[9]
,[Date].[Calendar].[Month].&[2007]&[10]
}
SET [AllCountries] AS
[Country].[Country].MEMBERS
SET [MthsCountries] AS
[2months] * [AllCountries]
SET [AllProds] AS
[Product].[Product].[Product].MEMBERS
SET [Top5Prods] AS
Generate
(
[MthsCountries] AS A
,{
//The top x prods
TopCount
(
NonEmpty
(
A.Current * [AllProds]
,[Measures].[Internet Sales Amount]
)
,5
,[Measures].[Internet Sales Amount]
)
}
)
MEMBER [Product].[Product].[All].[RestOfProds] AS
Aggregate
(
([Date].[Calendar].CurrentMember,[Country].CurrentMember) * [AllProds]
-
[Top5Prods]
)
SELECT
{[Measures].[Internet Sales Amount]} ON COLUMNS
,Generate
(
[MthsCountries] AS X
,
Order
(
Intersect
(
X.Current * [AllProds]
,[Top5Prods]
)
,[Measures].[Internet Sales Amount]
,bdesc
)
+
{X.Current * {[Product].[Product].[All].[RestOfProds]}}
) ON ROWS
FROM [Adventure Works];
EXISTING when used on named sets, doesn't really make a difference since named sets are created before the axes members are laid out. In your case, GENERATE is really just creating static sets and then on the axes everything is just cross joined.
To get the TopCount script working, you need to handle all the cross joins inside one set so that everything is evaluated together. I am not sure, but you might try the below:
WITH
SET [AllCountries] AS [Country].[Country].MEMBERS
SET [AllCats] AS [Product].[Category].[Category].MEMBERS
SET [Top5Cats] AS
Generate
(
[AllCountries] as a
,
{
(a.current , [Product].[Category].[All] ) //The ALL member
+
TopCount //The top 2 categories
(
NonEmpty((a.current * [AllCats] ) , (a.CURRENT, [Measures].[Internet Order Count]))
,2
,[Measures].[Internet Order Count]
)
}
+
{ //The rest of the members
a.current * [AllCats]
-
a.current *{
TopCount
(
NonEmpty([AllCats] , (a.CURRENT, [Measures].[Internet Order Count]))
,2
,[Measures].[Internet Order Count]
)
}
}
)
MEMBER [Product].[Category].[All].[RestOfProds] AS
Aggregate({(EXISTING {[AllCats]} - [Top5Cats])})
SELECT
{[Measures].[Internet Order Count]} ON COLUMNS,
[Top5Cats] ON ROWS
FROM [Adventure Works];
EDIT:
If you need the RestOfCats member, you can add this code.
SET [RestOfCats] AS
EXTRACT
(
{
[AllCountries] * [AllCats]
-
[Top5Cats]
},
[Product].[Category]
)
MEMBER [Product].[Category].[All].[RestOfCats] AS
Aggregate([RestOfCats])
EDIT 2
Continuing on your example, removing the 'All' member additionally in the definition.
SET [RestOfProds] AS
Extract
(
{[AllCountries] * [AllProds]} - [Top5Prods] - [AllCountries]*[Product].[Product].[All]
,[Product].[Product]
)
If you are crossjoining multiple hierarchies in the same dimension only valid combinations will be shown. This is called auto-exists and it explains why the first query works like you want.
When crossjoining hierarchies from different dimensions the auto-exists does not happen. I think this explains the odd combinations returned in the second query.
Try adding NON EMPTY at the beginning of your ON ROWS definition. Then it will only return combinations which have an Internet Order Count measure value (whatever measures are on columns). That's the proper way to limit the crossjoin.
If you don't want a NON EMPTY on Internet Order Count then so you need a NonEmpty(, [Measures].[A Different Measure]) on a different measure?
Edit: It looks like the issue is not related to auto-exists but is related to performing a different TopCount per country. So this info may help someone else but isn't the answer to this question.
This seems to work ok:
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];

Filtered Parameters MDX error

New to mdx and trying to filter one parameter. I have the following,
WITH MEMBER [Measures].[ParameterCaption] AS IIF(IsEmpty([Measures].[Charge Count]),null, [Payment].[Description].CurrentMember .MEMBER_CAPTION)
MEMBER [Measures].[ParameterValue] AS IIF(IsEmpty([Measures].[Charge Count]), null, [Payment].[Description].CurrentMember.UniqueName)
MEMBER [Measures].[ParameterLevel] AS IIF(IsEmpty([Measures].[Charge Count]), null, [Payment].[Description].CurrentMember .Level .Ordinal)
SELECT
{ [Measures].[ParameterCaption],
[Measures].[ParameterValue],
[Measures].[ParameterLevel] } ON COLUMNS
, NONEMPTY ([Primary].[Description].ALLMEMBERS, [Measures].[Charge Count]) ON ROWS
FROM (SELECT (STRTOSET(#Client, CONSTRAINED) ON COLUMNS
FROM [DACUBE]))
with an error on the second to last line with the 'ON'. This all seems correct except this error. Can someone tell me where I'm going wrong?
I think your braces are not quite correct - try the following:
WITH
MEMBER [Measures].[ParameterCaption] AS
IIF
(
IsEmpty([Measures].[Charge Count])
,null
,[Payment].[Description].CurrentMember.Member_Caption
)
MEMBER [Measures].[ParameterValue] AS
IIF
(
IsEmpty([Measures].[Charge Count])
,null
,[Payment].[Description].CurrentMember.UniqueName
)
MEMBER [Measures].[ParameterLevel] AS
IIF
(
IsEmpty([Measures].[Charge Count])
,null
,[Payment].[Description].CurrentMember.Level.Ordinal
)
SELECT
{
[Measures].[ParameterCaption]
,[Measures].[ParameterValue]
,[Measures].[ParameterLevel]
} ON COLUMNS
,NonEmpty
(
[Primary].[Description].ALLMEMBERS
,[Measures].[Charge Count]
) ON ROWS
FROM
(
SELECT
StrToSet
(#Client
,CONSTRAINED
) ON COLUMNS
FROM [DACUBE]
);

Ordering a complex set

I've been playing with a script of Chris Webb's found here: http://cwebbbi.wordpress.com/2007/06/25/advanced-ranking-and-dynamically-generated-named-sets-in-mdx/
The adapted script is this:
WITH
SET MyMonths AS
TopPercent
(
[Date].[Calendar].[Month].MEMBERS
,20
,[Measures].[Reseller Sales Amount]
)
SET MyEmployees AS
[Employee].[Employee].[Employee].MEMBERS
SET MyMonthsWithEmployeesSets AS
Generate
(
MyMonths
,Union
(
{[Date].[Calendar].CurrentMember}
,StrToSet
("
Intersect({},
{TopCount(MyEmployees, 10, ([Measures].[Reseller Sales Amount],[Date]. [Calendar].CurrentMember))
as EmployeeSet"
+
Cstr(MyMonths.CurrentOrdinal)
+ "})"
)
)
)
MEMBER [Employee].[Employee].[RestOfEmployees] AS
Aggregate
(
Except
(
MyEmployees
,StrToSet
(
"EmployeeSet" + Cstr(Rank([Date].[Calendar].CurrentMember,MyMonths))
)
)
)
MEMBER [Measures].[EmployeeRank] AS
Rank
(
[Employee].[Employee].CurrentMember
,StrToSet
(
"EmployeeSet" + Cstr(Rank([Date].[Calendar].CurrentMember,MyMonths))
)
)
SELECT
{
[Measures].[EmployeeRank]
,[Measures].[Reseller Sales Amount]
} ON 0
,Hierarchize
(
Union
(
Filter
(
MyMonthsWithEmployeesSets * MyEmployees //<<<HERE<<<<
,
[Measures].[EmployeeRank] >= 1
)
,{
MyMonthsWithEmployeesSets * [Employee].[Employee].[RestOfEmployees]
}
)
) ON 1
FROM [Adventure Works];
How do I ORDER the output so that each set of ten employees is in order 1 to 10, with the MEMBER called RestOfEmployees always found in position 11 i.e. it should follow the member ranked 10 ?
The temptation is to add the ORDER function at the point marked HERE i.e. that line will become :
MyMonthsWithEmployeesSets * ORDER(MyEmployees, [Measures].[EmployeeRank])
Doing that results in the following error message:
The dimension '[EmployeeSet0]' was not found in the cube when the string, [EmployeeSet0], was parsed.
I believe this is because the measure EmployeeRank uses the inline sets created during the generate function.
If you redefine EmployeeRank to be 11 for RestOfEmployees, you can just add that member to the set that is the first argument to Filter, and than apply Order, as it will sort after position 1 to 10:
WITH
SET MyMonths AS
TopPercent
(
[Date].[Calendar].[Month].MEMBERS
,20
,[Measures].[Reseller Sales Amount]
)
SET MyEmployees AS
[Employee].[Employee].[Employee].MEMBERS
SET MyMonthsWithEmployeesSets AS
Generate
(
MyMonths
,Union
(
{[Date].[Calendar].CurrentMember}
,StrToSet
("
Intersect({},
{TopCount(MyEmployees, 10, ([Measures].[Reseller Sales Amount],[Date]. [Calendar].CurrentMember))
as EmployeeSet"
+
Cstr(MyMonths.CurrentOrdinal)
+ "})"
)
)
)
MEMBER [Employee].[Employee].[RestOfEmployees] AS
Aggregate
(
Except
(
MyEmployees
,StrToSet
(
"EmployeeSet" + Cstr(Rank([Date].[Calendar].CurrentMember,MyMonths))
)
)
)
MEMBER [Measures].[EmployeeRank] AS
IIF([Employee].[Employee].CurrentMember IS [Employee].[Employee].[RestOfEmployees],
11,
Rank
(
[Employee].[Employee].CurrentMember
,StrToSet
(
"EmployeeSet" + Cstr(Rank([Date].[Calendar].CurrentMember,MyMonths))
)
)
)
SELECT
{
[Measures].[EmployeeRank]
,[Measures].[Reseller Sales Amount]
} ON 0
,
Order(
Filter
(
MyMonthsWithEmployeesSets
* UNION(MyEmployees, {[Employee].[Employee].[RestOfEmployees]})
,
[Measures].[EmployeeRank] >= 1
), [Measures].[EmployeeRank]
)
ON 1
FROM [Adventure Works];

MDX custom consolidation: consolidate only last level descendants

I'm looking for a elegant MDX expression that sum values only of the elements of last level dimension:
I have a measure M , also I have a hierarchical parent - child dimension U that is non balanced tree:
R -> ( M = R1 + R2 = 157 )
..R1 -> ( M = R11 + R12 = 150 )
...R11 -> ( M=R111 = 100 )
.....R111 -> M=100
...R12 -> M = 50
..R2 -> M = 7
I have a set that contains some elements from this dimension:
S contains R11, R111, R12
Now I need to take, for a U.currentMember the M value (that is, the sum of last level descendants)
I have written this expression, it works but perhaps they are a more elegant way to write it:
with member measures.XX as
sum (
intersect(
[S],
Except(
descendants( [U].currentMember ),
existing( descendants( [U].currentMember ).item(0) )
)
)
,
[M]
)
select
measures.xx on columns
from [CUBE]
where [U].[R]
Note: This MDX dont run:
with member measures.XX as
sum (
intersect(
[S],
descendants( [U].currentMember )
)
,
[M]
)
select
measures.xx on columns
from [CUBE]
where [U].[R]
because return 250 insteat 150.
Right result is 150: R11 + R12 (because R111 is included in R11).
Bad result is: 250: '100' value is taked for twice R11 + R111.
Final Solution:
with member measures.XX as
sum(
intersect (
descendants([U].currentMember,,leaves),
[S]
)
,
[M]
)
select
measures.XX on 0,
descendants( [Unitats].[Unitat].[All] ) on 1
from [C]
Not sure what you want to calculate but let's assume [Member] is the member you want to evaluate :
I'd use the descendants, filter and isLeaf MDX functions :
Sum(
Filter( Descendants( [Member] ), isLeaf(Member_hierarchy.currentmember) )
,[M])
You're adding all descendants including itself that are leafs (no children).