Dividing tied RANKs using MDX - ssas

Still getting the hang of interpreting the MDX documentation on MSDN. So for the RANK function it has the following:
If a numeric expression is specified, the Rank function determines the
one-based rank for the specified tuple by evaluating the specified
numeric expression against the tuple. If a numeric expression is
specified, the Rank function assigns the same rank to tuples with
duplicate values in the set. This assignment of the same rank to
duplicate values affects the ranks of subsequent tuples in the set.
For example, a set consists of the following tuples, {(a,b), (e,f),
(c,d)}. The tuple (a,b) has the same value as the tuple (c,d). If the
tuple (a,b) has a rank of 1, then both (a,b) and (c,d) would have a
rank of 1. However, the tuple (e,f) would have a rank of 3. There
could be no tuple in this set with a rank of 2. If a numeric
expression is not specified, the Rank function returns the one-based
ordinal position of the specified tuple. The Rank function does not
order the set.
In the following script if two people are tied second for the highest salary I get the following salary ranks:
1
2
2
4
What I'd like to do is use the number of years in service to decide which of the tied people has the higher rank. Is this possible?
WITH
SET [OrderedPeople] AS
ORDER(
NONEMPTY(
[PeopleDimension].[PeopleHier].[NamesLevel].members,
{ //following means if one or the other is null
//then the row is not excluded
[Measures].[Salary],
[Measures].[NumYearsService]
}
),
[Measures].[Salary]
*
[Measures].[NumYearsService]
,
BDESC
)
MEMBER [Measures].[Salary_Rank] AS
RANK([PeopleDimension].[PeopleHier].CurrentMember,
[OrderedPeople],
[Measures].[Salary] //<<<how do I use numYearsService to decide ties?
)
SELECT
NON EMPTY
{
[Measures].[NumYearsService],
[Measures].[Salary],
[Measures].[Salary_Rank]
}
ON COLUMNS,
NON EMPTY
[OrderedPeople]
ON ROWS
FROM [ourCube]
WHERE
(
{TAIL([Date].[Date - Calendar Month].[Calendar Day],7)(0):
TAIL([Date].[Date - Calendar Month].[Calendar Day],7)(6)}
)

If you have the set already ordered, you use Rank without the third argument, i. e.
RANK([PeopleDimension].[PeopleHier].CurrentMember,
[OrderedPeople]
)
Rank returns the position that the first argument has in the set which is the second argument. The third argument is specifically used for the case when you want to have ties getting the same value. If you use the third argument, then for adjacent elements within the set, the third argument is checked, and the return value is the position of the first element within the set that has the same value for the third argument.
To order by several criteria in MDX, nest two orders within each other:
ORDER(
ORDER(
NONEMPTY(
[PeopleDimension].[PeopleHier].[NamesLevel].members,
{ //following means if one or the other is null
//then the row is not excluded
[Measures].[Salary],
[Measures].[NumYearsService]
}
),
[Measures].[NumYearsService]
,
BDESC
),
[Measures].[Salary],
BDESC
)
AS MDX Order is guaranteed to do a stable sort, when executing the outer sort, then the members that have the same salary do not change their relative order from the first sort, which means they keep being sorted by years in service.

Related

DAX formula for - MAX of COUNT

I have the below dataset:
using the measure:
BalanceCount := COUNT(Balances[Balance])
which gives me the result:
However, I want the Grand Total to show the maximum amount of the BalanceCount, which is 2.
NewMeasure:=
MAXX(
SUMMARIZE(
FactTable
,FactTable[Account]
,FactTable[MonthEnd]
)
,[BalanceCount]
)
SUMMARIZE() groups by the columns specified, and MAXX() iterates through the table specified, returning the maximum of the expression in the second argument evaluated for each row in its input table.
Since the filter context will limit the rows of the fact table, we'll only have the appropriate subsets in each column/row grand total.
I found a solution that works for this particular case. It will not work if columns other than Account and MonthEnd are included in the filter context.
MaxBalanceCount:=
MAXX ( SUMMARIZE (
Balances,
Balances[Account],
Balances[MonthEnd]
),
CALCULATE ( COUNTROWS ( Balances ) )
)

MDX date range with NON EMPTY clause is not slicing the data for the range

I am working to optimize the query time. But in my cases, case 1 and case 2 are returning the same result. case 1 should return result for a particular data range i.e. [time].[2015].[5].[10] : [time].[2015].[6].[9] instead it is returning only for [time].[2015].[6].[9] as the result of case 2. case 1 returns the correct result i.e. data for the range but only if you remove the non empty clause. removing the non empty clause means it will search the entire data sets which is again taking lots of time. again case 3 returns correct result but operation is even more time then the first approach. Anybody facing such issue or can guide me to for the problem.
CASE 1
WITH
MEMBER [Measures].[abc_type] AS
[abc].CurrentMember.Properties("abc_type")
MEMBER [Measures].[abc_desc] AS
[abc].CurrentMember.Properties("abc_desc")
MEMBER [Measures].[abc_class] AS
[abc].CurrentMember.Properties("abc_class")
SELECT
NON EMPTY
{
[Measures].[abc_type]
,[Measures].[abc_desc]
,[Measures].[abc_class]
} ON COLUMNS
,NON EMPTY
Filter
(
{[abc].[abc_id].MEMBERS}
,St_contains
(
[district].[district_id].[1].Properties("the_geom")
,[abc].CurrentMember.Properties("the_geom")
)
) ON ROWS
FROM [analytics_cube]
WHERE
[time].[2015].[5].[10] : [time].[2015].[6].[9];
CASE 2
WITH
MEMBER [Measures].[abc_type] AS
[abc].CurrentMember.Properties("abc_type")
MEMBER [Measures].[abc_desc] AS
[abc].CurrentMember.Properties("abc_desc")
MEMBER [Measures].[abc_class] AS
[abc].CurrentMember.Properties("abc_class")
SELECT
NON EMPTY
{
[Measures].[abc_type]
,[Measures].[abc_desc]
,[Measures].[abc_class]
} ON COLUMNS
,NON EMPTY
Filter
(
{[abc].[abc_id].MEMBERS}
,St_contains
(
[district].[district_id].[1].Properties("the_geom")
,[abc].CurrentMember.Properties("the_geom")
)
) ON ROWS
FROM [analytics_cube]
WHERE
[time].[2015].[6].[9];
CASE 3
WITH
MEMBER [Measures].[abc_type] AS
[abc].CurrentMember.Properties("abc_type")
MEMBER [Measures].[abc_desc] AS
[abc].CurrentMember.Properties("abc_desc")
MEMBER [Measures].[abc_class] AS
[abc].CurrentMember.Properties("abc_class")
MEMBER [time].[newtime] AS
Aggregate([time].[2015].[5].[10] : [time].[2015].[6].[9])
SELECT
NON EMPTY
{
[Measures].[abc_type]
,[Measures].[abc_desc]
,[Measures].[abc_class]
} ON COLUMNS
,NON EMPTY
Filter
(
{[abc].[abc_id].MEMBERS}
,St_contains
(
[district].[district_id].[1].Properties("the_geom")
,[abc].CurrentMember.Properties("the_geom")
)
) ON ROWS
FROM [analytics_cube]
WHERE
[time].[newtime];
Ranges of time are always evaluated by Mondrian as a full level scan. It will load all of the members of the given time level and will start iterating over the members until it finds the first bound. It will then create a sublist up until the last member of the range.
There is an enhancement request filed to turn ranges into SQL predicates here.
If you get a chance to test the prototype code, let us know how it works for you

MDX - Is it possible to have two unrelated dimension members in one row?

I need to create the table of the following structure in MDX (to be used in SSRS report):
For that I have 2 dimensions and one measure:
Option dimension, with option type and option value attributes
Standard dimension, with IsStandard flag
Price measure
In first column I need to show all option type attributes,
in second all value attributes where IsStandard flag is set to [Y],
in third values chosen by user in parameters and
in fourth prices for components selected by user.
Is it possible to do the above in single MDX? Or will I be better off creating 2 distinct queries and creating 2 tables for them?
EDIT: Since my updates won't fit into the comment, I will add some thoughts here.
EXISTS function from answer below does not filter the result set, I don't get standard values but all possible values concatenated. When I issue the following code:
SELECT
[Measures].[Price] ON 0,
NON EMPTY [Option].[Option Type].children
*
[Option].[Option Value].children ON 1
FROM [Cube]
WHERE
(
[Standard].[IsStandard].&[Y],
[Configurations].[Configuration].&[conf1]
)
It returns the default values correctly, but if I use
SELECT
[Measures].[Price] ON 0,
[Option].[Option Type].children
*
EXISTS(
[Option].[Option Value].[Option Value].members
,([Standard].[IsStandard].&[Y],[Configurations].[Configuration].&[conf1])
) ON 1
FROM [Cube]
It does not filter the results.
If you can accept a slightly different order of columns, then this can be done in MDX, using a calculated measure which is actually a string (as you want to see a list of attributes values in column). This avoids having the same attribute twice in the rows:
WITH Member Measures.[Standard Value] AS
Generate(NonEmpty([Option].[Option Type].[Option Type].Members,
{([Standard].[IsStandard].&[Y],
Measure‌​s.[Price]
)}
),
[Option].[Option value].CurrentMember.Name,
", "
)
SELECT { Measures.[Standard Value], Measures.[Price] }
ON COLUMNS,
NON EMPTY
[Option].[Option Type].[Option Type].Members
*
{ #chosenValues } // the parameters value should be a comma separated list like "[Option].[Option value].[AMD], [Option].[Option value].[INTEL]"
ON ROWS
FROM [Your Cube]
WHERE [Configurations].[Configuration].&[conf1]
You can adapt the list separator (the last argument of the Generate function) to anything you like.
And in case there is more than one measure group that is related to the dimensions [Option], [Standard], and [Configurations], you should add the name of the measure group to use for determining the relationship as additional last parameter to the Exists, so that you and not the engine determines that. Just use the name of the measure group in either single or double quotes.
Yes it is, dimension will just be ignored. This is assuming you've all in the same schema / cube.
Note, depending on the OLAP Server you're using it's possible you've to change a flag that sends an error if you're using a dimensions that is not defined at Measure Group level.

Can RANK function be used anywhere other than the WITH clause

In a previous post I had this script:
WITH
SET [orderedSet] AS
ORDER(
[Operator].members,
[Operator].currentmember.name,
BASC
)
MEMBER [Measures].[newMeasure] AS
RANK(
[Operator].currentmember,
[orderedSet]
)
SELECT
[Measures].[newMeasure] ON COLUMNS,
[orderedSet] ON ROWS
FROM [ourCube]
Plus further reference to the MSDN page
Can the RANK function be used in any clauses other than WITH?
It's first argument is a tuple so I'm not sure how to use it in other clauses such as the SELECT.
It can be used anywhere where a numeric expression can be used.
Please note that in MDX, the axes in the select clause are sets, hence you cannot use Rank or any function that returns a numeric expression in an axis clause, but only functions returning sets (or some data types like tuples which are implicitly converted to sets).
And all members that appear in an expression returning a set have to be defined before you start with this expression. Hence, you cannot define them in the axis clause like you can use expressions to define result columns in SQL selects.
However, to literally answer your question, you can use Rank indirectly in the select clause in MDX e. g. if the outer function is Filter, which returns a set. The following is a slightly inefficient way to show the first three countries according to attribute order:
SELECT {[Measures].[Internet Sales Amount]}
ON COLUMNS,
Filter(
[Customer].[Customer Geography].[Country].Members as C,
Rank(C.Current, [Customer].[Customer Geography].[Country].Members) <= 3
)
ON ROWS
FROM [Adventure Works]
Some people use Rank within Generate to reverse sets, which would be another use of Rank that would be legal in the select clause.

Extracting second value from ordered list

The following gives me a list of the countries in alphabetical order.
How do I change it so that instead of returning the whole list it just returns the second country in this list? In terms of our cube this would just be the result for the Aland Islands.
SELECT
ORDER([Geolocation].[Geography].[Geography Country].MEMBERS,
[Geolocation].[Geography].CurrentMember.name, BASC ) ON ROWS,
[Measures].[DefaultMemberName] ON COLUMNS
FROM [MyCube]
You can use the item function; if I remember well it is 0-based :
ORDER( [Geolocation].[Geography] ... )(1)