MDX custom consolidation: consolidate only last level descendants - ssas

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

Related

DAX : optimize measure in SSAS 2012 tabular model

-I use SSAS 2012 SP4 tabular version.
The cube contains 2 facts tables and each one uses the same dimension tables (=7 dimension tables).
There is no relation defined between 2 fact tables.
-The following measure is setup in Fact2 table:
Measure1 :=
IF (
SUM ( Fact1[ColumnA] ) = 0,
BLANK (),
IF (
SUM ( Fact2[ColumnB] ) > 0,
SUM ( Fact1[ColumnA] ) / SUM ( Fact2[ColumnB] ),
99
)
)
My Excel report is very long to refresh when I display this measure and several attributes.
-When I active a profiler trace I can see that the time spend in the Formula Engine is 80%.
I have tried to rewrite the query with using the function DIVIDE() like this :
Measure1 := DIVIDE(sum(Fact1[ColumnA])/sum(Fact2[ColumnB]),99)
In this case the report is fast and query duration is better. But as I have removed the IF function I don't check any more "if sum(Fact1[ColumnA])=0"
It is possible to refactor this DAX formula in order to improve performance. And keeping the check "IF sum(Fact1[ColumnA])=0 THEN BLANK()" ?
Thanks a lot for your help
Em
Sure, try this:
Measure 1 :=
VAR SUMA =
SUM ( 'Fact1'[ColumnA] )
VAR SUMB =
SUM ( 'Fact2'[ColumnB] )
VAR QUOT =
DIVIDE ( SUM ( 'Fact1'[ColumnA] ), SUM ( 'Fact2'[ColumnB] ) )
RETURN
SWITCH ( TRUE (), SUMA = 0, BLANK (), SUMB > 0, QUOT, 99 )
If you can't use Variables in your measures, try this:
Measure1 :=
SWITCH (
TRUE (),
SUM ( 'Fact1'[ColumnA] ) = 0, BLANK (),
SUM ( 'Fact2'[ColumnB] ) > 0, DIVIDE ( SUM ( 'Fact1'[ColumnA] ), SUM ( 'Fact2'[ColumnB] ) ),
99
)
Another method would be to create a calculated column like so:
result := DIVIDE ( SUM ( 'Fact1'[ColumnA] ), SUM ( 'Fact2'[ColumnB] )
)
Then, write a measure against it like this:
Measure1 :=
SWITCH ( TRUE (), [result] = 0, BLANK (), [result] > 0, [result], 99 )
I haven't tested either of these so I am not sure how their performance will be.
Hope it helps!

DAX Cumulative VAMI Return

Here's a simplified version of my model:
I have P/L (profit and loss) data at the daily level for several funds and several securities. To calculate the % return is easy - it's the P/L divided by the opening AUM (assets under management). But each month the AUM changes significantly due to investor contributions. So, to get a YTD return %, I need to calculate cumulative returns, for which the formula is (see here):
YtdReturn =
PRODUCTX ( MonthlyReturnTable, DIVIDE ( MonthlyReturn, OpeningAUM ) +1 ) -1
The difficulty is that my P/L is at the daily level. I successfully created this:
CompoundReturn:=
PRODUCTX (
ADDCOLUMNS (
VALUES ( tblDates[MonthYearNumber] ),
"PnL Total1", CALCULATE ( SUM ( PnL[TradingPnL] ) + SUM ( PnL[InterestDividendsPnL] ) ),
"Month_Opening_AUM1", CALCULATE ( VALUES ( Daily_AUM[MonthOpeningAUM] ) )
),
DIVIDE ( [PnL Total1], [Month_Opening_AUM1] ) + 1
)
- 1
This works well:
However, I want to do this on a running YTD basis. I'm having trouble setting the "inner" table to YTD. I tried surrounding the VALUES() with a CALCULATETABLE() to filter by YTD, but didn't work:
CompoundReturn :=
PRODUCTX (
ADDCOLUMNS (
CALCULATETABLE (
VALUES ( tblDates[MonthYearNumber] ),
DATESYTD ( tblDates[Date] )
),
"PnL Total1", CALCULATE ( SUM ( PnL[TradingPnL] ) + SUM ( PnL[InterestDividendsPnL] ) ),
"Month_Opening_AUM1", CALCULATE ( VALUES ( Daily_AUM[MonthOpeningAUM] ) )
),
DIVIDE ( [PnL Total1], [Month_Opening_AUM1] ) + 1
)
- 1
The result in Excel is not a running return, but individual:
Any ideas? Thanks!
Note: All code formatted with daxformatter.com
Figured it out with the help of a friend. I needed to put the CALCULATETABLE outside the ADDCOLUMNS:
CompoundReturn :=
PRODUCTX (
CALCULATETABLE (
ADDCOLUMNS (
VALUES ( tblDates[MonthYearNumber] ),
"PnL Total1", CALCULATE ( SUM ( PnL[TradingPnL] ) + SUM ( PnL[InterestDividendsPnL] ) ),
"Month_Opening_AUM1", CALCULATE ( VALUES ( Daily_AUM[MonthOpeningAUM] ) )
),
DATESYTD ( tblDates[Date] )
),
DIVIDE ( [PnL Total1], [Month_Opening_AUM1] ) + 1
)
- 1

OrientDB how to get a result set of vertices and its edges in one query

I have been playing around with OrientDB sql queries to get a result set that contains not only vertices but also the internal edges that exists between them.
The query could be expressed as:
I want all the vertices that are related to project (without project itself) and all the edges between the vertices that are included in the results
Here is how I have achieved it but I think it is not the proper way to do it.
select expand($union) let $vertices = ( select from ( traverse both() from (select from V where label = 'project') ) skip 1 ), $edges = ( select from ( select from E where #rid in ( select bothE() from ( select from ( traverse both() from (select from V where label = 'project') ) skip 1 ) ) ) where out in ( select from ( traverse both() from (select from V where label = 'project') ) skip 1 ) and in in ( select from ( traverse both() from (select from V where label = 'project') ) skip 1 ) ), $union = unionall($vertices, $edges)
And the expected results:
Problems with this solution:
I have to traverse the graph multiple times (first to get the vertices and then to get the edges to finally merge the results)
The base query select from V where label = 'project' is also executed several times.
Is there a better way to solve this use case?
Thanks.
Try this query:
select expand($c)
let $a=(traverse both(),bothE() from (select from V where label="project")),
$b=(traverse bothE() from (select from V where label="project")),
$c=difference($a,$b)

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

Measure lacking context

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