I have to create the next calculation for 20 different Measures in my OLAP. As you can see the described function calculates the value of a measure the previous year. I have to repeat the code 20 times only changing the specific measure ([Measures].[Sales]) and I think there is a way of changing the specific measure dynamically.
create MEMBER CURRENTCUBE.[Measures].[Sales ant] as
(ParallelPeriod(
[Dim Calendar].[Calendar].[Year]
,1
,[Dim Calendar].[Calendar].CurrentMember
), [Measures].[Sales]),
VISIBLE = 1 ,ASSOCIATED_MEASURE_GROUP = 'Fact Sales';
Is there is a way in MDX to know which measure in the [Measures] group is being used? In this case which is the best way to implement it.
If I remember correctly you can create hidden set of measures like this:
CREATE HIDDEN STATIC SET CURRENTCUBE.[My Set Of Measures] AS
{
[Measures].[Measure 1],
[Measures].[Measure 2],
[Measures].[Measure 3]
};
Then you can create scope assignment for this set:
Scope ([My Set Of Measures]);
this = (ParallelPeriod(
[Dim Calendar].[Calendar].[Year]
,1
,[Dim Calendar].[Calendar].CurrentMember
), Measures.CurrentMember)
End Scope;
Note that instead of concrete measure - [Measures].[Sales] you will reference a measure with Measures.CurrentMember, so when you choose [Measures].[Sales] on your report the scope above will apply.
Hope it will work.
Related
I was able to calculate PY Total for Selected Time Dimension's Attributes in individual Hierarchy but cannot figure out how to make it work for both Time Dimension Hierarchies with single calculated measure.
I had to USE Dynamic SET to populate Days and use AGGREGATE on the SET as follows -
CREATE DYNAMIC SET CURRENTCUBE.[Days]
AS {EXISTING [Time].[Time].[Day]};
CREATE MEMBER CURRENTCUBE.[Measures].[PY Sales Selected]
AS AGGREGATE(
GENERATE(EXISTING [Days],
{PARALLELPERIOD([Time].[Time].[Year],1,[Time].[Time].CurrentMember)}
),
[Measures].[Sales]
),
FORMAT_STRING = "#,#",
VISIBLE = 1
----
CREATE DYNAMIC SET CURRENTCUBE.[Days]
AS {EXISTING [Time].[Current].[Curr Day Flag]};
CREATE MEMBER CURRENTCUBE.[Measures].[PY Sales Selected]
AS AGGREGATE(
GENERATE(EXISTING [Days],
{PARALLELPERIOD([Time].[Current].[Curr Year Flag],1,[Time].[Current].CurrentMember)}
),
[Measures].[Sales]
),
FORMAT_STRING = "#,#",
VISIBLE = 1
Both calculations work individually but I would like to combine them so that I could just have one Calculated Measure [Measures].[PY Sales Selected].
We have following Time dimension structure -
I would appreciate any help!!
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 an ordinary DimProduct which has a snowflaked DimPriceList where I store for each ProductKey different prices for the products.
In a MDX, depending on certain product properties, I would like to display different prices for the products. The MDX would look something like this.
CASE
WHEN [Dim Product].[Value Group].CurrentMember.MEMBERVALUE = '02' THEN
CASE
WHEN [Dim Product].[Stock Account Nr].CurrentMember.MEMBERVALUE = 101 THEN
([Dim Price List].[Sales1])
WHEN [Dim Product].[Stock Account Nr].CurrentMember.MEMBERVALUE = 102 OR
[Dim Product].[Stock Account Nr].CurrentMember.MEMBERVALUE = 103 THEN
([Dim Price List].[Valuation])
ELSE
0
END
ELSE
1
END
How can I now access the current membervalue of the DimPriceList? I just get 'All' as result and not the actual value for the current member. I've also tried [Dim Price List].[Valuation].CurrentMember.MEMBERVALUE but with no success. Appreciate any sort of help or hints.
You get the [All] member because it IS the default value. In absence of any scope, the engine bases it's calculation around the default member. If you want the output to change based on the current member of Product, you should attempt at creating a calculated member. Encapsulate the logic above into the script which chooses the requisite measure depending upon the current member. I am assuming you must be having Sales1 and Valuation in form of Measure in your cube. If that is the case your calculated member should choose the requisite measure based on the MEMBERVALUE or you can employ
....
[Dim Product].[Stock Account Nr].CurrentMember IS [Dim Product].[Stock Account Nr].&[101]
....clause.
That said, you need to give some scope.
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:
I have a simple olap cube - one set of measures and some unexciting dimensions.
I've add one calculation to get the "percent of total" sales against the gross sales measure. The code for this calculation is:
([Dim Stores].[Store Name].CurrentMember, [Measures].[Gross Sales])
/
([Dim Stores].[Store Name].Parent, [Measures].[Gross Sales])
This works.
Within the store dimension, there is a hierarchy called 'By State' where the stores are contained within.
Two questions please:
1. Any idea why the calculation would not work when I use the the 'By state' hierarchy i.e. the same calculation grouped by the next level up?
The state problem aside, any idea why my grand total shows an error even when I just use the Store Name?
TIA!
In poking around, I found a template within the "calculation tools" called "Percentage of Total". Using it, I translated my calculation to this:
Case
// Test to avoid division by zero.
When IsEmpty
(
[Measures].[Gross Sales]
)
Then Null
Else ( [Dim Stores].[By State].CurrentMember, [Measures].[Gross Sales] )
/
(
// The Root function returns the (All) value for the target dimension.
Root
(
[Dim Stores]
),
[Measures].[Gross Sales]
)
End
It worked!