Sum part of hierarchy based on second attribute from same dimension - ssas

so I have this dimension which has a ragged hierarchy. The hierarchy is nice to navigate but a royal pain to search in (Excel is the front end, so you have to search manually in all 12 or so levels).
We created a separate "search" attribute with all the members of the hierarchy to search in - however, in this flat list the leaves will contain the proper data but the intermediate nodes will of course not be rolled up (in lack of hierarchical information)
My idea was to put a formula on the flat list to go look up its equivalent member in the hierarchy and get its value from there.
Here's what I have - both approaches don't work unfortunately:
With
------ APPROACH 1: DESCENDANTS
Member [m1] As
Sum(
Descendants(
Filter(
[Dimension].[Hierarchy].Members
, [Dimension].[Hierarchy].Properties("Key") =
[Dimension].[Flat List].CurrentMember.Properties("Key")
),, LEAVES), ([Measure].[MeasureHierarchy].CurrentMember, [Measures].[Amount]))
------- APPROACH 2: StrToMember + CHILDREN
Member [m2] As
Sum(
StrToMember("[Dimension].[Hierarchy].&["+
[Dimension].[Flat List].CurrentMember.Properties("Key")+
"]").Children,
([Measure].[MeasureHierarchy].CurrentMember, [Measures].[Amount])
)
Select
{ [m1], [m2] }
On 0,
[Dimension].[Flat List].&[838]
dimension properties member_key
On 1
From [Cube]
Where [Measure].[MeasureHierarchy].[SomeMeasure]
Both will always return null - if I query the Hierarchy directly, it works - just not if I use the flat list
Any ideas?

figured it out myself - had a spark of inspiration tonight :-D
the answer is as simple as:
with member
[m1] as
(
StrToMember("[Dimension].[Hierarchy].&["+
[Dimension].[Flat List].CurrentMember.Properties("Key")+
"]")
, [Dimension].[Flat List].[All]
)
...
Maybe that will help someone else too

Related

MDX where clause in subquery does not slice cube - how to understand?

This query gives me sales of one store:
select
[measures].[sales] on 0
from [MyCube]
where [store].[store].[042]
However, if I move the slicer to inside of the subquery, it gives me sales of all stores.
select
[measures].[sales] on 0
from (select
from [MyCube]
where [store].[store].[042]
)
How to understand the mechanisms behind this difference?
This is also noted in this article, but without much explanation.
----EDIT----:
I tried various things and read around for a while. I'd like to add a question: is there a scenario in which the where clause in sub-select does filter the result?
This query gives me sales of all stores in state MI (store [042] belongs to MI):
select
[measures].[sales] on 0
from (select
[store].[state].[MI] on 0
from [myCube]
where [store].[store].[042]
)
Thinking of 'inner query only filters if the filtered dimension is returned on an axis', the theory is proved wrong if I do this:
select
[measures].[sales] on 0
from (select
[store].[state].members on 0
from [myCube]
where [store].[store].[042]
)
The sub-select still returns one state MI, but the outer query returns sales of all stores (of all states).
----EDIT 4/13----:
Re-phrasing the question in AdventureWorks cube with screenshot.
Query 1: sales of one store
Query 2: it returns sales of all stores if where clause is in the sub-select.
Query 3: the two answers I got suggested that we select the dimension in an axis - here is the result - we get all cities.
select
[measures].[sales] on 0
from (select
from [MyCube]
where [store].[store].[042]
)
The above query reduces the scope of stores just to the member [042]. Make note that sub-select is executed before the actual select. So, when it comes to the select, the engine just sees a cube which has all the members in all the dimensions; but only the member [store].[store].[042] in the store dimension. It's as if the cube has been kept intact every where else but sliced off on the Store dimension.
If you go a step ahead and add the store on to one of the axes, like
select
[measures].[sales] on 0,
[store].[store].members on 1
from (select
from [MyCube]
where [store].[store].[042]
)
you would see that although the member [All] appears in the output, it actually is just comprised of only one store.
In essence, the [All] is a special member which is calculated with respect to scope of the cube. It reflects the combined effect of all the members in the cube.
In SQL terms, it is similar to:
select sales, store as [All] from
(select sales, store from tbl where store = '042') tbl
Even though you see Sales----All, it is but a reflection of sales for store [042]
Here are some other good references concerning sub-select and slicer debate:
http://bisherryli.com/2013/02/08/mdx-25-slicer-or-sub-cube/
https://cwebbbi.wordpress.com/2014/04/07/free-video-on-subselects-in-mdx/
Chris Webb's video being located here:
https://projectbotticelli.com/knowledge/what-is-a-subselect-mdx-video-tutorial?pk_campaign=tt2014cwb
This should still leave an All member:
SELECT
[measures].[sales] ON 0
FROM
(
SELECT
FROM [MyCube]
WHERE
[store].[store].[042]
);
...but the member [All] of the Store hierarchy will only now be made up of [store].[store].[042].
You can see this by adding the Store hierarchy onto ROWS:
SELECT
[measures].[sales] ON 0,
[store].MEMBERS ON 1
FROM
(
SELECT
FROM [MyCube]
WHERE
[store].[store].[042]
);
This is the AdvWorks version similar to the reference in your question:
SELECT
{[Measures].[Order Count]} ON 0
,[Subcategory].MEMBERS ON 1
FROM
(
SELECT
{
[Subcategory].[Subcategory].&[22]
} ON 0
FROM [Adventure Works]
);
It returns the member from the sub-select and the All member adjusted to take account of the subselect:
In the references article why is the [All] less than the sum of the other two - this is not down to the subselect but is in connection with the measure that he has chosen [Measures].[Order Count] which is a distinct count. If you take away the subselect you see exactly the same behaviour of the All member being less than the sum of the other subcategory members (I've marked the point at which the total of the parts becomes higher than the All member):
SELECT
{[Measures].[Order Count]} ON 0
,Order
(
[Subcategory].MEMBERS
,[Measures].[Order Count]
,bdesc
) ON 1
FROM [Adventure Works];
Order Count: on 1 order there might be several Product Subcategories - hence this behaviour.
Edit
This query of yours:
select
[measures].[sales] on 0
from (select
[store].[state].members on 0
from TestCube //<< added this!
where [store].[store].[042]
)
This inner script is not valid? Using the same dimension on an axes and the WHERE clause is not valid:
select
[store].[state].members on 0
from TestCube
where [store].[store].[042]
Edit2
An mdx script returns a cube, which may be sliced or not sliced, but nevertheless it returns a cube. The WHERE clause is used to slice the cube that is returned. If we were using a third party tool then the dimension added to the WHERE clause would go into a combobox - with say Cliffside selected. BUT the user could effectively select Ballard from that combobox - it is just a slicer. The WHERE clause is not changing the cube that is returned by the mdx script, it is just affecting what is displayed in the cellset.
WHERE is valid within a subselect. It is part of the definition:
https://msdn.microsoft.com/en-us/library/ff487138.aspx
I've never found a use case for a subselect's WHERE clause.
Edit3
This link will explain things:
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/ccb66ac3-0f9a-4261-8ccc-b6ecc51b6f07/is-where-clause-pointless-inside-a-subselect?forum=sqlanalysisservices
As Darren gosbell says in the answer to this question:
https://msdn.microsoft.com/en-us/library/ff487138.aspx it says that:
The WHERE clause does not filter the subspace.

Cross join same hierarchy columns

I have a SQL data cube with following hierarchy
I want to cross join Warehouse division and Code warehouse Desc. I wrote a MDX as follows
SELECT NON EMPTY
{ [Measures].[Total Value]}
DIMENSION PROPERTIES CHILDREN_CARDINALITY,
PARENT_UNIQUE_NAME ON COLUMNS,
NON EMPTY
{
[Combined].[Drill Down Path 4].[Warehouse Division].MEMBERS* [Combined].[Drill Down Path 4].[Code Warehouse Desc].MEMBERS
}
DIMENSION PROPERTIES MEMBER_CAPTION ON ROWS FROM [InventoryAge]
WHERE ( [Calendar].[Report Days].[All Members].&[All].&[WantInReport].& [2].&[20141031] )
It gives me an error as follows
Query (13, 8) The Drill Down Path 4 hierarchy is used more than once in the Crossjoin function.
Can any body suggests a better way to do this
Please find the calender hierarchy
You don't need to crossjoin hierarchy (this is impossible) to do what you need. Just query the lowest level of it, you will get all parents also
SELECT NON EMPTY
{ [Measures].[Total Value]}
DIMENSION PROPERTIES CHILDREN_CARDINALITY,
PARENT_UNIQUE_NAME ON COLUMNS,
NON EMPTY
{
[Combined].[Drill Down Path 4].[Code Warehouse Desc].allMEMBERS
}
DIMENSION PROPERTIES MEMBER_CAPTION ON ROWS FROM [InventoryAge]
WHERE ( [Calendar].[Report Days].[All Members].&[All].&[WantInReport].& [2].&[20141031] )
PS. You might not be able to see them in SSMS query result viewer, but they will appear if you use query in cube browser or as dataset in SSRS/other tool
You can pass last date in your set by using Tail(Existing [Calendar].[Report Days].[All Members].&[All].&[WantInReport].members, 1).item(0)
Or you can use Format(Now()), which gives you string representation of current system date. However, it depends on locale, so you probably need to remove dots/slashes. See here

Calculated SSAS Member based on multiple dimension attributes

I'm attempting to create a new Calculated Measure that is based on 2 different attributes. I can query the data directly to see that the values are there, but when I create the Calculated Member, it always returns null.
Here is what I have so far:
CREATE MEMBER CURRENTCUBE.[Measures].[Absorption]
AS sum
(
Filter([Expense].MEMBERS, [Expense].[Amount Category] = "OS"
AND ([Expense].[Account Number] >= 51000
AND [Expense].[Account Number] < 52000))
,
[Measures].[Amount - Expense]
),
VISIBLE = 1 , ASSOCIATED_MEASURE_GROUP = 'Expense';
Ultimately, I need to repeat this same pattern many times. A particular accounting "type" (Absorption, Selling & Marketing, Adminstrative, R&D, etc.) is based on a combination of the Category and a range of Account Numbers.
I've tried several combinations of Sum, Aggregate, Filter, IIF, etc. with no luck, the value is always null.
However, if I don't use Filter and just create a Tuple with 2 values, it does give me the data I'd expect, like this:
CREATE MEMBER CURRENTCUBE.[Measures].[Absorption]
AS sum
(
{( [Expense].[Amount Category].&[OS], [Expense].[Account Number].&[51400] )}
,
[Measures].[Amount - Expense]
),
VISIBLE = 1 , ASSOCIATED_MEASURE_GROUP = 'Expense';
But, I need to specify multiple account numbers, not just one.
In general, you should only use the FILTER function when you need to filter your fact table based on the value of some measure (for instance, all Sales Orders where Sales Amount > 10.000). It is not intended to filter members based on dimension properties (although it could probably work, but the performance would likely suffer).
If you want to filter by members of one or more dimension attributes, use tuples and sets to express the filtering:
CREATE MEMBER CURRENTCUBE.[Measures].[Absorption]
AS
Sum(
{[Expense].[Account Number].&[51000]:[Expense].[Account Number].&[52000].lag(1)} *
[Expense].[Amount Category].&[OS],
[Measures].[Amount - Expense]
),
VISIBLE = 1 , ASSOCIATED_MEASURE_GROUP = 'Expense';
Here, I've used the range operator : to construct a set consisting of all [Account Number] members greater than or equal to 51000 and less than 52000. I then cross-join * this set with the relevant [Amount Category] attribute, to get the relevant set of members that I want to sum my measure over.
Note that this only works if you actually have a member with the account number 51000 and 52000 in your Expense dimension (see comments).
An entirely different approach, would be to perform this logic in your ETL process. For example you could have a table of account-number ranges that map to a particular accounting type (Absorption, Selling & Marketing, etc.). You could then add a new attribute to your Expense-dimension, holding the accounting type for each account, and populate it using dynamic SQL and the aforementioned mapping table.
I don't go near cube scripts but do you not need to create some context via the currentmember function and also return some values for correct evaluation against the inequality operators (e.g.>) via the use of say the membervalue function ?
CREATE MEMBER CURRENTCUBE.[Measures].[Absorption]
AS sum
(
[Expense].[Amount Category].&[OS]
*
Filter(
[Expense].[Account Number].MEMBERS,
[Expense].[Account Number].currentmember.membervalue >= 51000
AND
[Expense].[Account Number].currentmember.membervalue < 52000
)
,
[Measures].[Amount - Expense]
),
VISIBLE = 1 , ASSOCIATED_MEASURE_GROUP = 'Expense';
EDIT
Dan has used the range operator :. Please make sure your hierarchy is ordered correctly and that the members you use with this operator actually exist. If they do not exist then they will be evaluated as null:
Against the AdvWks cube:
SELECT
{} ON 0
,{
[Date].[Calendar].[Month].&[2008]&[4]
:
[Date].[Calendar].[Month].&[2009]&[2]
} ON 1
FROM [Adventure Works];
Returns the following:
If the left hand member does not exist in the cube then it is evaluated as null and therefore open ended on that side:
SELECT
{} ON 0
,{
[Date].[Calendar].[Month].&[2008]&[4]
:
[Date].[Calendar].[Month].&[1066]&[2] //<<year 1066 obviously not in our cube
} ON 1
FROM [Adventure Works];
Returns:

Arbitrarily picking a dimension to add members to

The following script gives exactly the result I want.
It feels like a hack as I've added the custom members VALUE and VALUE_MTD onto the hierarchy [Customer].[Country]. I've chosen this hierarchy arbitrarily - just not used [Measures] or [Date].[Calendar] as they are already in use.
Is there a more standard approach to returning exactly the same set of cells?
WITH
MEMBER [Customer].[Country].[VALUE] AS
Aggregate([Customer].[Country].[(All)].MEMBERS)
MEMBER [Customer].[Country].[VALUE_MTD] AS
Aggregate
(
PeriodsToDate
(
[Date].[Calendar].[Month]
,[Date].[Calendar].CurrentMember
)
,[Customer].[Country].[VALUE]
)
SELECT
{
[Customer].[Country].[VALUE]
,[Customer].[Country].[VALUE_MTD]
} ON 0
,NON EMPTY
{
[Measures].[Internet Sales Amount]
,[Measures].[Internet Order Quantity]
}
*
Descendants
(
{
[Date].[Calendar].[Month].&[2007]&[12]
:
[Date].[Calendar].[Month].&[2008]&[01]
}
,[Date].[Calendar].[Date]
) ON 1
FROM [Adventure Works];
The standard approach is called utility dimension. If you Google this term, you will find several descriptions of this approach. A "utility dimension" is one which does not reference any data, but is just added to the cube for the purpose of being able to cross join them with all other dimensions for calculations. You can have one or more of them.
Thus, in most cases, physically there is nothing in the dimension. It is just used for calculated members. (Depending on the implementation, you may have the attribute members defined physically, if you want to have some properties for them. But then, only the default member is referenced in the star schema from the fact tables. The attribute member values are then overwritten in the calculation script.)
Typical applications for this are time calculations like YTD, MTD, MAT (Moving Annual Total, i. e. a full year of data ending in the selected date), or comparisons like growth vs. a previous period.

How to create a calculated member based on two measures and the hierarchy level of the queried dimension's current member?

I have a cube which has
two measure members: [Measures].[Value] (integer) and [Measures].[EffectiveBelowLevel] (integer).
a dimension called [DimParentChild] with a ragged user hierarchy called [ParentChildHierarchy].
I would like to create a calculated member on the measures dimension ([Measures].[EffectiveValue]) based on [Measures].[Value] which when queried along [DimParentChild] and [ParentChildHierarchy] behaves as follows:
- [Measures].[Value] is used if the hierarchy level of [DimParentChild].[ParentChildHierarchy].CURRENTMEMBER > [Measures].[EffectiveBelowLevel].
- 0 is used if the hierarchy level of [DimParentChild].[ParentChildHierarchy].CURRENTMEMBER <= [Measures].[EffectiveBelowLevel].
Is it possible to achieve this functionaly with a calcuated member on the measures dimension?
If yes then what the formula would look like?
If not then what other way would there be?
I am very interested in any other kind of solution as well (e.g. an mdx query, etc.)
As an example:
[Measures]
[Value] [EffectiveBelowLevel] ParentChildAssociation
10 1 GrandChild1
20 2 GrandChild2
[DimParentChild].[ParentChildHierarchy]
Member HierarchyLevel Description
Parent 1 -
Child 2 first child of Parent
GrandChild1 3 first child of Child
GrandChild2 3 second child of Child
With this data [Measures].[EffectiveValue] should look like this
ParentChild EffectiveValue
Parent 0
Child 10
GrandChild1 10
GrandChild2 20
How about something along the lines (I'm not sure about level ordinal being 0-based):
with member xx as
Sum( [DimParentChild].[ParentChildHierarchy].currentMember as myCurrentMember,
Sum( Descendants( myCurrentMember(0), 64, LEAVES ),
IIF( myCurrentMember(0).level.ordinal > [EffectiveBelowLevel], [Value], 0 )
)
)
select [xx] on 0, [DimParentChild].[ParentChildHierarchy].members on 1 from [...]
You can have a look to this MDX documentation here for more details.
I see you have posted this question here also (saw it originally on ssas msdn forum), so I am providing the link to my answer as it might help other people. thread link on SSAS msdn forum
#Marc - As this is a case of parent child dimension and p/c dimensions can have data associated on nonleaf members your query would not return the correct results. It took me some time to figure out how to aggregate the correct results from children in this case and recommend you have a look at the link. Offtopic: good luck with your product, I hope I'll get the time to evaulate it one day :)
Regards,
Hrvoje