Following on from this question regarding calculating a member I have 2 calculated members defined as:
MEMBER [Asset].[Class].[Fixed Income Derivatives]
AS
AGGREGATE(
{
[Asset].[Class].&[Fixed Income],
[Asset].[Sub Class].&[Derivatives]
},
[Measures].CurrentMember
)
MEMBER [Asset].[Class].[Fixed Income Non-derivatives]
AS
AGGREGATE(
{
[Asset].[Class].&[Fixed Income],
EXCEPT([Asset].[Sub Class].[Sub Class],[Asset].[Sub Class].&[Derivatives])
},
[Measures].CurrentMember
)
I can use these in an MDX query as follows:
SELECT
{
[Measures].[Market Value]
} ON 0,
NON EMPTY
{
[Asset].[Class].[Fixed Income Derivatives],
[Asset].[Class].[Fixed Income Non-derivatives]
[Asset].[Class].[Class]
} ON 1
FROM [Asset]
And this gives me output as follows:
Class-----------------------|-MarketValue
============================|=============
Fixed Income Derivatives | 12345
Fixed Income Non-derivatives| 54321
Fixed Income | 66666
Property | 123
Equity | 987
Note that the first 2 rows are actually constituant parts of Row 3. Now, I can do some magic with the client code which reads this data to turn that table into a hierarchy, but - and here's the question - can I do it using MDX? Or am I just complicating things? Im not adverse to making changes in the cube if necessary, or if I could define this hierarchy there.
AFAIK, you can't use MDX to get a result that is supposed as hierarchy at the client side without some magic with the client code or changing the cube structure. Even though you can find a trick to fake the client the result as an hierarchy i don't suggest you to go that way.
Here is my suggestion.
If i understand correctly, you want to get a result that is kind of an unbalanced hierarchy tree that looks like below.
[Asset].[Class]
[Fixed Income]
[Asset].[Class].[Fixed Income Derivatives]
[Asset].[Class].[Fixed Income Non-Derivatives]
Property
Equity
This kind of unbalanced trees can only be implemented by parent-child hierarchies.
You can either try to balance the hierarchy by adding an intermediate level (or leaf levels) that includes Property and Equity members or create a parent-child hierarchy to achieve even deeper level unbalanced tree.
Edit
Here is an article about parent-child dimensions.
I ended up just returning both Class and Sub Class, and building the hierarchy in client code.
Perhaps adding the calculated member this way:
WITH MEMBER [Asset].[Class].[Fixed Income].[Derivatives] AS ...
Related
Has anybody ever ported a complex ssas multidmensional cube to iccube? and have any advice to offer on lessons learnt/gaps between the two tools etc?
The major one i can see is scope(). What's the equivalent in iccube? nested if/case statements?
I have a list here. Anything else?
function | SSAS | iccube
------------------------|------------------------|------------------------
multi threaded calcs | no | yes
------------------------|------------------------|------------------------
fix/scope block code | SCOPE() | ??
------------------------|------------------------|------------------------
custom functions | clr.net but it's slow | mdx+
------------------------|------------------------|------------------------
generic date utility | third-party code that | ??
dimensions (eg generic | uses scope() eg |
prior period/prior | datetool |
corresponding period) | |
------------------------|------------------------|------------------------
We have a very mdx script calculation heavy cube, and the single threaded nature of the SSAS calculation engine is a real bottleneck. None of the other olap tools we've looked at have been fast enough or had a rich enough language
We use disconnected utility dimensions to drive the functionality, and need to have a date utility dimension (we use a version of this http://sqlmag.com/sql-server-analysis-services/optimizing-time-based-calculations-ssas), use AXIS() extensively, and have recursive sum product across descendents of a hierarchy for a non-additive measure.
Our cube isn't a self service reporting cube. It's a multidimensional calculation engine for our app with a fixed generic schema
Update 1: A simpler example of how we use scope. ic, You mention 'robust' workarounds exist. What would they be for code like this?
// Assumes the date utility dim has been setup as with the priorperiod function as [Dim Date Calculations].[Date Calculations].[Prior Period]
// DimBenchmark is a single attribute disconnected utility dimension. The initial/default value is DimBenchmark.Benchmark.None ie do nothing. The remainder are dynamically set based on code. hardcoded below for simplicity
Scope(DimBenchmark.BenchMark.PriorPeriod);
THIS = [Dim Date Calculations].[Date Calculations].[Prior Period]; // assign the value of some physical and utility dim members to new benchmark attributes. Allows us to only refer to dimbenchmark in subsequent code, irrespective of number of benchmarks or the src dimension.attribute
END SCOPE;
SCOPE(DimBenchmark.BenchMark.Budget);
THIS = DimScenario.Scenario.Budget; //we also have a budget
END SCOPE;
.... // any number of other benchmarks
Create measure currentcube.measures.ComplexCalc as NULL;
SCOPE (measures.ComplexCalc); // this code will only change how complex calc behaves
SCOPE (DimBenchmark.Benchmark.All - DimBenchmark.Benchmark.None); // this will only change the ComplexCalc when the active benchmark selection is not "none"
this= (some measure,Complex subcube etc);
End Scope;
End Scope;
the benefit of this is that complexcalc is null by default. it only gets a value when it meets specific conditions. the main reason to use scope is for speed. Much faster than if/case blocks (and simpler understand)
i dont need to explicitly define which benchmarks are valid, just which benchmark isn't.
and below is how we've implemented the date utility dimension. It allows us to do something like
(measure,[Dim Date Calculations].[Date Calculations].[Prior Period]) and it gives use the prior period of measure for the current member of dim date (month goes back 1 month, quarter goes back 3 months, semester goes back 6 mo, year goes back 12 mo). It's very clean, accurate and pretty fast.
-- Fiscal Month
Scope( [Dim Date].[Month Key].[Month Key].members);
-- Prior Period
Scope([Dim Date Calculations].[Date Calculations].[Prior Period]);
this =
( [Dim Date].[Month Key].CurrentMember.PrevMember
,[Dim Date Calculations].[Date Calculations].[Current]
);
END scope;
End Scope;
-- Fiscal Quarter
Scope( [Dim Date].[Fiscal Quarter].[Fiscal Quarter].members);
-- Prior Period
SCOPE( [Dim Date Calculations].[Date Calculations].[Prior Period]);
THIS = ( [Dim Date].[Fiscal Quarter].CurrentMember.PrevMember
,[Dim Date Calculations].[Date Calculations].[Current]
);
END SCOPE;
END SCOPE;
-- Fiscal Semester
Scope( [Dim Date].[Fiscal Semester].[Fiscal Semester].members);
-- Prior Period
SCOPE( [Dim Date Calculations].[Date Calculations].[Prior Period]);
THIS = ( [Dim Date].[Fiscal Semester].CurrentMember.PrevMember
,[Dim Date Calculations].[Date Calculations].[Current]
);
END SCOPE;
End Scope;
-- Fiscal Year
Scope( [Dim Date].[Fiscal Year].[Fiscal Year].members);
-- Prior Period
SCOPE( [Dim Date Calculations].[Date Calculations].[Prior Period]);
THIS =
( [Dim Date].[Fiscal Year].CurrentMember.PrevMember
,[Dim Date Calculations].[Date Calculations].[Current]
);
END SCOPE;
End Scope;
[disclaimer I'm working for icCube]
Scope is not part of icCube, not yet planned. It's a tricky feature that didn't naturally fit into icCube's architecture (see discussion below, later ... ). The strength of icCube is also the agility of it's R&D team, do not hesitate to contact them directly they will me more than happy to improve and add features.
In icCube there are some functionalities that are different from classical MDX server that might be useful, it's Categories, SubCubes and the eval function.
Categories. Allows for defining a new member that behaves like classical members. This new member can be defined as a set of members or as a subcube. For example here we can define a [Top10] category member as our 10 most important customers :
CATEGORY MEMBER [Top10] as TopCount( [Customers], 10, ([Measures].[Sales],[2015]) )
-> so later on ( [Top10], [Sales] ) will return the sales of this top10 customers
SubCubes, allows defining richer logical relations on members as a set of tuples. icCube implements SubCubeComplement, SubCubeIntersect,SubCubeOthers, SubCubeSymDifference, SubCubeUnion and SubCubeMinus. So calculating all without France (it's trivial here, but think on hierachies with many-to-many relations)
SubCubeMinus([Geography].[Geo].[All], [Geography].[Geo].[France] )
The Eval function, allows for evaluating an expression on a subCube. Here is a trivial example doing the sum using the union :
MEMBER [US+CH] AS Eval( SubCubeUnion( [Switzerland], [United States]) , [Amount])
Last but not least, for the dates function you can define Function in icCube that you can reuse in your MDX, no need to copy&paste everywhere :
CREATE FUNCTION square( Value val ) AS val * val
and combine with CompactSet for a faster evaluation on dates periods (if there are no m2m relations on this dimension) or call some Java functions (you've to activate this module that is off by default).
--------------------- Scope ---------------------------
Warning : Comments might be obsolete as my understanding of scope is the one of a few years ago. Let's go :
Scope is a nice functionality but there are some drawbacks that you can check in Chris Webb's presentation ( link ), check from 47:30 for about 5 minutes.
The issues where/are :
Somehow a scope allows to define a new value for a subcube (remember a subcube might be a single indivisible cell as well as thousands of them)
1) Scopes allows for defining the value of a subcube but what do you do if you want a 'part' of this subcube ?
2) What happens if two scopes collide (intersection is not empty) ?
And all this mixed with security, many-to-many relations, subqueries and set where clauses.
We're not SSAS specialist and it's possible that with a better understanding we try again to implement a clean solution but for know we believe there are other ways solving the problems (for example using calc. members or writebacks).
hope it helps.
i have a huge table of cashflows that means there are +int values for income and -int values for outcome.
I have MeasureGroup for Sum the amount of money.
I now want to display not only the sum of money per month but also the sum of all the past time until the current month so like that:
Month MoneyAmount Total
1 20 20
2 -10 10
3 5 15
4 -10 5
So i know for the first part its just like
select [Measures].[Money] on 0,
[Date].[Month].Members on 1
From MyCube
but how can i add the sum column?
i thought about something like SUM( { NULL : [Date].[Month].CurrentMember } , [Measures].[Money] ) but that didnt work as well :(
In MDX, the total is already there. You do not have to do complex calculations to get it.
But it depends on your exact hierarchy structure how the All member is called. If you have a date user hierarchy named [Date].[Date], and it has a month level named [Date].[Date].[Month], then the all member of the hierarchy would probably be called something like [Date].[Date].[All]. If [Month] is an attribute hierarchy of the Date dimension, then the "all member" would probably be called [Date].[Month].[All]. In the latter case, the all member would already be the first member of the set [Date].[Month].Members. As you are asking the question, I am assuming this is not the case, and you are using a user hierarchy. Then you could change your MDX query to
select [Measures].[Money] on 0,
Union([Date].[Month].Members, { [Date].[Date].[All] }) on 1
From MyCube
Please note that you can change the name of the All member in the property settings of a dimension when designing an Analysis Services dimension, hence I cannot know the definitive name without knowing the details of this setting in your cube. So you might have to adapt the name of the all member.
You can find this name out in SQL Server Management Studio in an MDX window as follows: open the hierarchy that you are using, and then open the "Members" node, below which you should find the "All Member". You can drag this into your MDX statement, and the proper name will appear there.
As in a running sum?
You need a calculated measure, like this:
With Member [Measures].[Running Sum] as Sum( ( [Date].[Months].Members.Item(0) : [Date].[Months].CurrentMember ), [Measures].[Money])
Select [Date].[Months].Members on Rows,
{[Measures].[Money], [Measures].[Running Sum] } on Columns
From [MyCube]
I have problem in my calculated member. Whenever this member involve in calculation or query it take large time to execute. I am trying to narrow down execution time.
I have to remove IIF condition from the members and start using scope instead.
CREATE Member CurrentCube.[Measures].[AvgAmount] as
IIF(ISLeaf([Customer].[ParentCustomer].currentmember),
[Measures].[Value],
(SUM([CCube^Customer].[ParentCustomer].CURRENTMEMBER.CHILDREN) /
COUNT([Customer].[ParentCustomer].CURRENTMEMBER.CHILDREN))
) ,
Format_String = "#.0000000;-#.0000000;0;0",
Non_Empty_Behavior = [Measures].[Amout];
I have created hierarchy of customer which is [ParentCustomer] here. I want to see avg amount of all the children under the parent customer but when I am looking child level which does not have any children in it should only show the [Measures].[Amout].
Thanks in advance
Regards,
Sam
From your question, I assume you really want to have the average of the children, and not the average of all leaf level descendants. The latter could be implemented as follows:
Create a new measure group on the customer dimension table which has a single measure 'customer count' which would just be implemented as count - or of your customer dimension as a granularity that is finer than a single customer - countdistinct of the customer key or something like this.
Then just define your measure as
CREATE Member CurrentCube.[Measures].[AvgAmount] as
[Measures].[Value] / [Measures].[customer count],
Format_String = "#.0000000;-#.0000000;0;0",
Non_Empty_Behavior = [Measures].[Amout];
This assumes that the aggregation of [Measures].[Value] is defined as sum or one of the semi additive aggregations, but not max or min or something similar.
However, I assume from your question that this is not what you want. Instead you want to see the average of the children at each level. And I assume that [Customer].[ParentCustomer] is a standard user hierarchy and not a parent child hierarchy. Then, the approach suggested in the title, using SCOPE, would work. Let's assume you have three levels in your [Customer].[ParentCustomer] hierarchy:
The (implicitly defined) All level, just containing the All member
level A, built from attribute A of the dimension
level B, which is the leaf level and built from attribute B of the dimension
Then, under similar assumptions about the [Measures].[Value] aggregation, you could define the AvgAmount measure as follows:
// create the measure as it is correct for level B:
CREATE Member CurrentCube.[Measures].[AvgAmount] as
[Measures].[Value],
Format_String = "#.0000000;-#.0000000;0;0",
Non_Empty_Behavior = [Measures].[Amout];
// overwrite the definition for level A:
SCOPE([Customer].[ParentCustomer].[A].Members);
[Measures].[AvgAmount] = [Measures].[Value] / (EXISTING [Customer].[B].[B].Members).Count
END SCOPE;
// overwrite the definition for the ALl level:
SCOPE([Customer].[ParentCustomer].&[All]);
[Measures].[AvgAmount] = [Measures].[Value] / (EXISTING [Customer].[A].[A].Members).Count
END SCOPE;
This approach, using SCOPE, would not work for a parent child hierarchy, buta syou do not write you have one, I just assume you don`t.
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
I’m pretty new to the many-to-many dimensions but I have a scenario to solve, which raised a couple of questions that I can’t solve myself… So your help would be highly appreciated!
The scenario is:
There is a parent-child Categories dimension which has a recursive Categories hierarchy with NonLeafDataVisible set
There is a regular Products dimension, that slices the fact table
There is a bridge many-to-many ProductCategory table which defines the relation between the two. Important to note is that a product can belong to any level of the categories hierarchy – i.e. a particular category can have both – directly assigned products and sub-categories.
There is a fact Transactions table that holds a FK to the Product that has been sold, as well as a FK to its category. The FK is needed, because
I have all this modeled in BIDS, the dimension usage is set between each of the dimensions and the facts, the many-to-many relation between the Categories and the Transactions table is in place is in place. In other words everything seems kind of OK..
I now need to write an MDX which I would use to create a report that shows something like that:
Lev1 Lev2 Lev3 Prod Count
-A
-AA 6
-AA 2
P6 1
P5 1
-AAA 2
P1 1
P2 1
-AAB 2
P3 1
P4 1
+BB
The following MDX almost returns what I need:
SELECT
[Measures].[SALES Count] ON COLUMNS,
NONEMPTYCROSSJOIN(
DESCENDANTS([Category].[PARENTCATEGORY].[Level 01].MEMBERS),
[Product].[Prod KEY].[Prod KEY].MEMBERS,
[Measures].[Measures].[Bridge Distinct Count],
[Measures].[SALES Count],
2) ON ROWS
FROM [Sales]
The problem that I have is that for each of the non-leaf categories, the cross join returns a valid intersection with each of the products that’s been sold for it + all subcategories. Hence the result set contains way too much redundant data and besides I can’t find a way to filter out the redundancies in the SSRS report itself.
Any idea on how to rewrite the MDX so that it only returns the result set above?
Another problem is that if I create a role-playing Category dimension which I set to slice directly the transactions data, then the numbers that I get when browsing the cube are completely off… It seems as SSAS is doing something during processing (but it’s not the SQL statements it shoots to the OLTP, as those remain exactly the same) that causes the problem, but I’ve no idea what. Any ideas?
Cheers,
Alex
I think I found a solution to the problem, using the following query:
WITH
MEMBER [Measures].[Visible] AS
IsLeaf([DIM Eco Res Category].[PARENTCATEGORY].CurrentMember)
MEMBER [Measures].[CurrentProd] AS
IIF
(
[Measures].[Visible]
,[DIM Eco Res Product].[Prod KEY].CurrentMember.Name
,""
)
SELECT
{
[Measures].[Visible]
,[Measures].[CurrentProd]
,[Measures].[FACT PRODSALES Count]
} ON COLUMNS
,NonEmptyCrossJoin
(
Descendants
(
[DIM Eco Res Product].[Prod KEY].[(All)],
,Leaves
)
,Descendants([DIM Eco Res Category].[PARENTCATEGORY].[(All)])
,[Measures].[FACT PRODSALES Count]
,2
)
DIMENSION PROPERTIES
MEMBER_CAPTION
,MEMBER_UNIQUE_NAME
,PARENT_UNIQUE_NAME
,LEVEL_NUMBER
ON ROWS
FROM [Sales];
In the report then I use the [Measures].[CurrentProd] as a source for the product column and that seems to work fine so far.