I'm setting up a new cube in Analysis Services, and used the Business Intelligence wizard to work out the currency conversion issues. Now this all works perfectly, money is converted at the leaf level and summed up for display in the user's choice of reporting currency.
My problem now is the calculation of liability. For liability, I need to sum up the money in each currency, then convert it using the most recent 'End of Day Rate'. I have 'End of Day Rate' as a LastNonEmpty measure, but I can't see how to avoid the leaf-level conversion as shown below:
// This is the Many to One section
// All currency conversion formulas are calculated for the pivot currency and at leaf of the time dimension
Scope ({ Measures.[Money] } );
Scope( Leaves([Date]) ,[Reporting Currency].[GBP], Leaves([Currency]));
// Convert Local value into Pivot currency for selected Measures that must be converted with Measure rate [End Of Day Rate]
Scope( { Measures.[Money] } )
This = [Reporting Currency].[Local] * Measures.[End Of Day Rate];
End Scope;
End Scope;
// This is the One to Many section
// All currency conversion formulas are calculated for the non pivot currency and at leaf of the time dimension
Scope( Leaves([Date]) , Except([Reporting Currency].[Currency].[Currency].Members, {[Reporting Currency].[Currency].[Currency].[GBP], [Reporting Currency].[Currency].[Currency].[Local]}));
// This section overrides the local values with the Converted value for each selected measures needing to be converted with Measure rate [End Of Day Rate]…
// LinkMember is used to reference the currency from the source currency dimension in the rate cube.
Scope( { Measures.[Money] } );
This = [Reporting Currency].[Currency].[GBP] / (Measures.[End Of Day Rate], LinkMember([Reporting Currency].[Currency].CurrentMember, [Currency].[Currency]));
End Scope;
End Scope; // Leaves of time, all reporting currencies but local and pivot currency
End Scope; // Measures
The [Money] measure is paid in in different currencies, and each currency is keyed to a currency dimension and an 'End of Day Rate.
What is my best plan for calculating the liability? I'm considering replicating the [Money] measure, but it seems wasteful to have extra measures in just to avoid the currency conversion - Plus in the real cube there are several more measures that require the calculation so it won't just be the extra one.
Anyone else faced something similar?
OK so I ended up creating an invisible [Measures].[Money - Liability] which wasn't auto-converted by the above code, and I ended up with the following calculation in the script:
[Measures].[Liability] =
(
SUM
( [Currency].[Currency].[Currency],
SUM
(
{ NULL : [Date].[Date Key].CurrentMember },
(
[Money - Liability]
)
)
/ [Measures].[End Of Day Rate]
)
* (Measures.[End Of Day Rate], LinkMember([Reporting Currency].[Currency].CurrentMember, [Currency].[Currency]))
);
Related
I am trying to apply a measure value calculated at the Month level to a dimension contained within that month i.e.
Should look like this:
I've attempted to use a scoping statement so far but with no luck.
SCOPE (
{[Sale].[Sale Year].&[2]:[Sale].[Sale Year].&[7]}
,[Date].[Calendar Month].&[201603]
,[Measures].[Costs Per Sale] );
THIS = ([Date].[Calendar Month].&[201603],[Measures].[Costs Per Sale]);
END SCOPE;
The Aggregated Sales measure is calculated using the Sale Year which unfortunately has not and cannot be linked to the Cost dimension.
Does anyone know how I can apply the Cost Per Sale monthly value to the [Sale].[Sale Year] dimension?
Thanks
Try this -
SCOPE ({[Sale].[Sale Year].&[2]:[Sale].[Sale Year].&[7]} ,[Measures].[Costs Per Sale]);
THIS = ([Sale].[Sale Year].[All],[Measures].[Costs Per Sale]);
END SCOPE;
Trying to figure out what the default member adds to this scipt
([DateTool].[Aggregation].[Previous Month]) =
iif(
([DateTool].[Aggregation].DefaultMember, ParallelPeriod(dim_RPT_period].[Yr-Qtr-Month].[RPT Period Month],1,[dim_RPT_period].[Yr-Qtr-Month].currentmember)) = null,
NULL,
([DateTool].[Aggregation].DefaultMember,ParallelPeriod([dim_RPT_period].[Yr-Qtr-Month].[RPT Period Month],1,[dim_RPT_period].[Yr-Qtr-Month].currentmember))
);
This is from a utility dimension. Calculation script.
I get the IIF, and I get the ParallelPeriod.
But what effect does "(DefaultMember, ParallelPeriod)" have?
It looks like it used to avoid infinite recursion. This technique is used in this kind of dynamic calculations.
Let's say we run some MDX script with WHERE [DateTool].[Aggregation].[Previous Month].
Server takes this calculation from your formula (without [DateTool].[Aggregation].DefaultMember), it uses ParallelPeriod as usual, but than it needs [DateTool].[Aggregation].[Previous Month] as a part of tuple with other dimensions (our WHERE filter), so it takes from this formula again and again...
So we always need to have some fixed member to avoid infinite recursion.
That is my understanding, please correct if it's somewhere or totally wrong.
My understanding is that the calculated member is built to have values only when the slicer has any member from [dim_RPT_period].[Yr-Qtr-Month].[RPT Period Month] level. This is my opinion imitates the ISCROSSFILTERED functionality of DAX.
The member is built such that [DateTool].[Aggregation].[Previous Month] would hold value only when
the slicer has a member from [dim_RPT_period].[Yr-Qtr-Month].[RPT Period Month]
[DateTool].[Aggregation].DefaultMember is as good as [DateTool].[Aggregation].[ALL]
It is as good as leaving out [DateTool].[Aggregation] hierarchy from the query
If slicer does not have a member from [dim_RPT_period].[Yr-Qtr-Month].[RPT Period Month],
then [dim_RPT_period].[Yr-Qtr-Month].currentmember is as good as [dim_RPT_period].[Yr-Qtr-Month].[ALL]
The previous member in this case(which is returned by ParallelPeriod function) is undefined and hence, NULL would be returned.
If slicer has the member, a non null member would be returned.
To further illustrate upon this:
(Please read through the comments)
([DateTool].[Aggregation].[Previous Month]) =
iif(
(
[DateTool].[Aggregation].[ALL],
ParallelPeriod([dim_RPT_period].[Yr-Qtr-Month].[RPT Period Month],1,[dim_RPT_period].[Yr-Qtr-Month].currentmember)
) //If a member from dim_RPT_period].[Yr-Qtr-Month].[RPT Period Month] level is not in slicer then it would evaluate to NULL
= null,
NULL, //In that case the previous month should evaluate to NULL
//Otherwise, it should give the "previous month"
([DateTool].[Aggregation].[ALL],
ParallelPeriod([dim_RPT_period].[Yr-Qtr-Month].[RPT Period Month],1,[dim_RPT_period].[Yr-Qtr-Month].currentmember))
);
I have got a measure, getting the last value of a currency exchange rate. The fact having the exchange rates, is configured in the dimension usage with the time dimension, and is daily based. So, using MDX, I am successfully getting my converted measure in my currencies, by using a calculated measure:
with member [Measures].[Calculated Comp Money In] as
SUM([Dim Time].[Date Key].CurrentMember,
[Measures].[Comp Money In]/[Measures].[Last Currency Rate])
And then in the where clause, I would filter out what currency I am reporting figures on
[Dim Currency].[Currency Key].&[200]
However, what I don't like is that I have a [Measures].[Comp Money In], and a [Measures].[Comp Money In Calculated].... Can I use the SCOPE function in MDX, so that [Measures.[Comp Money In] is configured with the calculation above? I would then add this calculation in the calculations section of SSAS.
If you use the Enterprise edition of SSAS, you can use the measure expression property of your measure do currency conversion. This is not very well documented in the Analysis Services documentation, but works as follows: You use only one measure (i. e. either [Measures].[Calculated Comp Money In] or [Measures].[Comp Money In], and in the "Measure expression" property of this measure, enter the expression that you have in your question above:
[Measures].[Comp Money In] / [Measures].[Last Currency Rate]
Then the Analysis Services engine will take care of the rest for you. Note that this expression may only contain the measure on which you define it, times or divided by one other measure (normally he exchange rate) and nothing else.
If you do not have The Enterprise edition of Analysis Services, you can more or less copy your definition to a calculated member:
To be able to continue using the existing measure, possibly rename the measure [Measures].[Comp Money In] to [Measures].[_Comp Money In] (and make that measure invisible when you have checked that everything is fine). Then define a calculated member with the same name as the original measure:
create member CurrentCube.[Measures].[Comp Money In] as
SUM([Dim Time].[Date Key].CurrentMember,
[Measures].[_Comp Money In]/[Measures].[Last Currency Rate])
Better still, use SCOPE to calculate the measure, and avoid the aggregation for single days: Assuming the only common dimension between the measure group containing [Measures].[Comp Money In] and the measure group containing [Measures].[Last Currency Rate] is [Dim Time] and this has the key attribute [Dim Time].[Date Key], and your date hierarchy is named [Dim Time].[Date], you could just use
CREATE MEMBER CurrentCube.[Measures].[Comp Money In] as NULL;
SCOPE([Dim Time].[Date Key].[Date Key].members);
[Measures].[Comp Money In] =
[Measures].[_Comp Money In] / [Measures].[Last Currency Rate];
END SCOPE;
SCOPE([Dim Time].[Date].Members);
[Measures].[Comp Money In] =
Sum(EXISTING [Dim Time].[Date Key].[Date Key].members);
END SCOPE;
I'm relatively new to MDX (in the process of ordering a book just now) and having a crack at creating a calculated measure, which should sum a value from one of my Fact tables, based on a set range of dates (from my Time dimension) which should be filtered by an attribute from another dimension. Unfortunately, I seem to have gotten myself in a bit of a situation and can't see a clear way out. I looked through other similar questions but didn't see any trying to do this same thing, only single dimension measures.
I have a Measure Group, which contains the physical measure SourceMeasure.
I have a Time dimension, named [Time], which I want to use to specify my range for the calculation. This is related to the above measure group by my Date Key.
I have an Account dimension, named [Dim Account], which contains two date attributes: [Account Start Date] and [Account End Date]. This is related to the above measure group via an Account ID.
What I'm trying to do, is filter the [Time] dimension to a range based on the [Account Start Date] and [Account End Date], and return the sum of [SourceMeasure] for the period specified for that account.
Obviously, this will be different for each account, so shouldn't be aggregated, except by the [Dim Account] dimension (perhaps returning 0 or null if not applied).
Example test expression below, this is currently not working due to an error with the FORMAT_STRING syntax (according to MDX Studio).
WITH
MEMBER [Measures].[LifetimeMeasure] AS
Sum
(
{
StrToMember
(
"[Time].[Date].&["
+ Format([Dim Account].[Account Start Date].CurrentMember.MemberValue,"yyyy-MM-ddThh:mm:ss")
+ "]"
)
:
StrToMember
(
"[Time].[Date].&["
+ Format([Dim Account].[Account End Date].CurrentMember.MemberValue,"yyyy-MM-ddThh:mm:ss")
+ "]"
)
}
,[Measures].[SourceMeasure]
)
SELECT
{
[Measures].[LifetimeMeasure]
,[Measures].[SourceMeasure]
}
ON COLUMNS
,{
[Dim Account].[Account].[AccountName]
}
ON ROWS
FROM
[MyCube]
I've tried a few different approaches (SCOPE, Filter, IIF) but every time I seem to get back to the same sticking point - how to filter the [Time] dimension based on the value of the [Dim Account] start and end dates.
Now, it may be that I'm completely misunderstanding how the relationships work between the dimensions and/or how calculated measures are computed by SSAS but I'm not yet at a level where I'm sure what to look for, so I'm going round in circles.
Is what I'm trying to do possible, and if so, any pointers as to where to look to help me figure out where I'm going wrong? Should I perhaps be looking to use a Calculation Dimension instead?
I hope this all makes sense, let me know if I've missed anything or if more detail is required. I'm testing using MDX Studio.
I found the error by adding the CONSTRAINED flag to the StrToMember function.
It seems that my [Time] dimension's date entries have defaulted to the time "00:00:00" while my [Dim Account] dimension's date entries have defaulted to "12:00:00". It looks like a mismatch between 24-hour and 12-hour clock. My date fields on the [Dim Account] dimension are marked as "Regular", not "Date" due to the fact that the dimension isn't a natural time dimension. I think it's potentially this that's causing the mismatch.
To workaround the issue for now, I've removed the Time format from my Format expression and just appended the "00:00:00" to the string while I search for the root cause. I'll update once I've solved it.
UPDATE: The cause of the mismatch is not in the dimension, but in the format string I've used in the StrToMember expression. It's not documented on MSDN, but the following string outputs 12-hour time format:
"yyyy-mm-ddThh:mm:ss"
While this string outputs 24-hour time format:
"yyyy-mm-ddTHH:mm:ss"
A simple cube has 1 measure and three time dimensions:
[Measures].[Amount Paid]
[Date Paid]
[Cover Start Date]
[Cover End Date]
Earned Premium =
0% if Cover Start Date is before the period in question
100% if Cover End Date has passed
else [Cover End - Cover Start] * Days Since Start
For any given cell, how do I traverse all the start and end dates and determine what the amount earned for a period is?
I assume other dimension are missing, e.g. one like [ContractId], if no what comes after doesn't make real sense.
The problem here is that your actual measure,earned premium, is a function (Amount Paid, Cover Start Date, Cover End Date, date) and this for each deal. You can not aggregate over a set of deals at once as the function is not associative - or something like this :-).
So I would feed my cube with the premium for each deal over the period [Cover Start Date], [Cover End Date] with the daily premium for this contract. Once you've this you can easily aggregate this measure over your dimensions. -> Now daily premium is not anymore a function of Cover dates..
MDX is not a real calculation engine, so you're pushing the system out of it's limit. Solving this with scopes, calculated measures can produce an amazingly slow cube...