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.
Related
I understand how to use the MDX Aggregate() and Sum() functions, and the differences between them.
(One interesting one is that the Sum of a measure defined at a higher level in a hierarchy over that level's Children multiplies the measure by the number of children - whereas Aggregate "correctly" returns just the value defined at the higher level).
The documented syntax on MSDN is:
Aggregate(Set_Expression [ ,Numeric_Expression ])
I've always used it with both arguments. But what does Aggregate do when only the set_expression argument is provided? The documentation (again from MSDN) is pretty obscure:
If a numeric expression is not provided, this function aggregates each measure within the current query context by using the default aggregation operator that is specified for each measure.
I tried it in an MDX query like this:
WITH MEMBER WeekSummedTotal AS
Aggregate([Days].[WeeksAndDays].CurrentMember.Children)
SELECT
{Measures.ThingoCount,Measures.WeekTotal,Measures.WeekSummedTotal} ON 0,
[Days].[WeeksAndDays].[WeekName] ON 1
FROM DateGRoupingTest
What would this do? Would Aggregate aggregate the cube's default measure over the set? Or the set Measures.Members? Or the set of other measures specified on the 0 axis?
None of these! The query runs and returns results, but the calcd measure WeekSumTotal shows #Error, with a completely nonsensical error:
Aggregate functions cannot be used on calculated members in the measures dimension
Now this is true, but completely irrelevant. None of the other measures in the query is calculated, and in fact the cube doesn't have any calculated members. So what is Aggregate() actually trying to do here? Is this error message (again, in MDX!) completely misleading?
ADDITION: #whytheq in the answer below suggested creating the calculated measure using Aggregate, but creating it on a spare dimension hierarchy rather than in the Measures dimension. This works, but only if the cross-join with the [All] member of the selected "any old..." dimension is included.
Creating the measure there also makes it impossible to put the two (base) measures and the calculated measure on the same axis. If I try to do this:
{Measures.ThingoCount,Measures.WeekTotal,[Ages].[Age Key].WeekSummedTotal} ON 0,
I get the deeply-unhelpful error message:
Members, tuples or sets must use the same hierarchies in the function.
which, I think, translates to "I can't make a set using the , (UNION) function between members of Measures and members of [Ages].[Age Key] because they're members of different dimensions".
My conclusion, thanks to your informative answers, is that Aggregate() with a single argument is a tricky beast; I wonder why it was designed with the second argument optional?
I've also noted that trying to create my calculated member on my Ages dimension (only one hierarchy, only one attribute) gives me the misleading error message:
The 'Ages' dimension contains more than one hierarchy, therefore
the hierarchy must be explicitly specified.
unless I explicitly specify the hierarchy. MDX has so much potential, but the learning curve would be that much gentler if MS had put more effort into making it feed back errors properly.
What would this do? Would Aggregate aggregate the cube's default
measure over the set? Or the set Measures.Members? Or the set of other
measures specified on the 0 axis?
Aggregate function aggregates the set over the current measure for Measures dimension. And a measure is "current" if it is in scope. If a measure is not in scope, the default member from measures dimension is considered for aggregation.
A measure can be added to scope in many ways like
Having the measure on axes
with member [Customer].[Customer].abc as
aggregate([Customer].[Customer].members)
select [Customer].[Customer].abc on 0,
{[Measures].[Internet Sales Amount],[Measures].[Reseller Sales Amount]} on 1
from [Adventure Works]
In the above example the member abc was calcualted twice, once for each measure.
Using Subcube
with member [Customer].[Customer].abc as
aggregate([Customer].[Customer].members)
select [Customer].[Customer].abc on 0
from (select {[Measures].[Internet Sales Amount] } on 0 from [Adventure Works])
Having the measure in definition
with member [Customer].[Customer].abc as
aggregate([Customer].[Customer].members, [Measures].[Internet Sales Amount])
select [Customer].[Customer].abc on 0
from [Adventure Works]
In Where clause
with member [Customer].[Customer].abc as
aggregate([Customer].[Customer].members)
select [Customer].[Customer].abc on 0
from [Adventure Works]
where [Measures].[Internet Sales Amount]
As suggested by whytheq, have the member on some other dimension-hierarchy combo. Otherwise, the aggregate function would probably lead to the calculated member self-referencing itself.
Taking this section of the MSDN definition:
...this function aggregates each measure within the current query
context ...
each measure in the context of your script is the following:
{Measures.ThingoCount,Measures.WeekTotal,Measures.WeekSummedTotal}
Now Measures.WeekSummedTotal is a calculated members in the measures dimension - hence the error.
I'd imagine something like the following would function ok, where you use Aggregate to create a member in a dimension other than Measures?:
WITH
MEMBER [SomeSpareDim].[SomeSpareHier].WeekSummedTotal AS
Aggregate
(
[Days].[WeeksAndDays].CurrentMember.Children * [SomeDim].[SomeHier].[All]
)
SELECT
[SomeSpareDim].[SomeSpareHier].WeekSummedTotal ON 0
,[Days].[WeeksAndDays].[WeekName] ON 1
FROM DateGRoupingTest;
The above can be changed to show Aggregate being very useful:
WITH
MEMBER [Days].[WeeksAndDays].[Last3Weeks] AS
Aggregate
(
{
[Days].[WeeksAndDays].[Weekx]
,[Days].[WeeksAndDays].[Weeky]
,[Days].[WeeksAndDays].[Weekz]
}
)
SELECT
{Measures.ThingoCount,Measures.WeekTotal} ON 0
,{
//<< the following custom aggregated member will work for any measure, that is ON 0, via Aggregate
//it can be mixed up with the normal members of the same hierarchy like the following
[Days].[WeeksAndDays].[Last3Weeks]
,[Days].[WeeksAndDays].[WeekName].members
} ON 1
FROM DateGRoupingTest;
The custom measure in the following is taken from the book MDX Cookbook (Tomislav Piasevoli):
WITH
MEMBER [Internet Sales PP] AS
Sum
(
Generate
(
{
[Date].[Calendar].[Date].&[20080105]
:
[Date].[Calendar].[Date].&[20080125]
}
,{
ParallelPeriod
(
[Date].[Calendar].[Calendar Year]
,1
,[Date].[Calendar].CurrentMember.Item(0)
)
}
)
,[Measures].[Internet Sales Amount]
)
SELECT
{
[Measures].[Internet Sales Amount]
,[Internet Sales PP]
} ON 0
,[Product].[Color].MEMBERS ON 1
FROM [Adventure Works];
What purpose does the item(0) serve?
My understanding, which is probably wrong is
<set>.item(0) gives us first tuple in set
<tuple>.item(0) gives us first member in tuple
So what is the point of <member>.item(0)?
Refer this excellent article on the topic.
To sum it up, when we are doing a .ITEM(0) on a member, that member is implicitly converted to a tuple. So, .ITEM(0) does not really serve any purpose other than returning the member itself.
I would assume this is a typo or a copy paste error. At least in the official Microsoft MDX reference, there are only the two Item() versions that you mention.
And this does not cause an error, as there are some implicit type conversions:
If you have a member and need a tuple for the current expression, AS implicitly builds a one-member tuple from the member. Which is what takes place here presumably, when applying Item(0) to a member.
If you have a one member tuple and need a member for the current expression, AS implicitly applies Item(0).
There are similar implicit conversions from tuple and level to set, from tuple to scalar value, from dimension to hierarchy, and from hierarchy to member.
I am attempting to return a measure two periods from the current member on the time dimension however I need to only include periods that match a certain criteria (Is_Business_Day = true)
I currently have:
(
[Date].[Calendar].CURRENTMEMBER.NEXTMEMBER.NEXTMEMBER,
[Measures].[SOME MEASURE]
)
Which accurately returns the value of the measure two members in the future, but now I need to additionally apply the filter, but can't quite figure out how to do so.
EDIT:
My thinking is that I would have to do something similar to the following
(
Head(
exists(
[Date].[Calendar].CurrentMember.NextMember:[Date].[Calendar].CurrentMember.Lead(6),
[Date].[Is Business Day].&[True]
),
2
).item(1),
[Measures].[SOME MEASURE]
)
As both hierarchies, [Date].[Calendar] and [Date].[Is Business Day] are in teh same dimension, you can rely on SSAS "Autoexists" which is usually faster than Exists. Hence,
((([Date].[Calendar].[Date].&[20050718].nextmember : null )
*
{ [Date]..Item(0) }
).Item(0)
,[Measures].[SOME MEASURE]
)
The : null construct builds a set to the end of the Date level, i. e. to the last day contained in the attribute.
Cross joining with [Date].[Is Business Day].&[True] automatically restricts the set to those members that co-exist in the dimension (the magic of autoexists).
And .Item(0) extracts the first tuple. In case you need a tuple not of the date, the .Item(0), and the measure member, but just of the date and the measure in your context, apply another Item(0) after the first. This would extract the first member from the tuple.
I am using Performance Point Dashboard Designer 2013 and SharePoint Server 2013 for building dashboards. I am using SSAS2012 for Cube.
I have a scenario similar to the one illustrated by figure below. I am required to find Previous Non-Empty value for purpose of finding Trends.
Measure: [Quota]
Dimension: [Date].[Calendar Date].[Date]
The script ([Measures].[Quota], [Date].[Calendar Date].PrevMember) gives you a previous date. Lets say for date 27-Jan-13 whose Quota value is 87, it returns 26-Jan-13 which has null value. I want it to return 21-Jan-13 that has some Quota value. And for date 21-Jan-13, I want to return 15-Jan-13.
I wonder if this is possible.
Thanks,
Merin
After long searches and hits & trials and so on, I think I invented a solution of my own for myself.
Following is the script for my Calculated Member.
(
[Quota],
Tail
(
Nonempty
( LastPeriods(15, [Date].[Calendar Date].PrevMember)
,[Quota]
)
).Item(0)
)
Explanation
The number 15 means it will look for non-empty measures up to 15 siblings.
Now we know up to how many siblings to traverse back, in this case 15.
Lets find 15 previous siblings (both empty and non-empty) excluding current member.
(LastPeriods(15, [Date].[Calendar Date].PrevMember)
Since it will yield both empty and non-empty members, lets filter out empty members in terms of measure [Quota]. If we don't specify measure here, it will use default measure whatever it is and we may not get desired result.
Nonempty(LastPeriods(15, [Date].[Calendar Date].PrevMember),[Quota])
We may have several members in the output. And we will choose the last one.
Tail
(
Nonempty
( LastPeriods(15, [Date].[Calendar Date].PrevMember)
,[Quota]
)
)
So far, the script above gives previous non-empty member. Now we want to implement this member for our measure [Quota].
Hence we get the script below ready to create a Calculated Member.
(
[Quota],
Tail
(
Nonempty
( LastPeriods(15, [Date].[Calendar Date].PrevMember)
,[Quota]
)
).Item(0)
)
You can use recursion to define this.
The following query delivers something similar for the Adventure Works cube:
WITH member [Measures].[Prev non empty] AS
IIf(IsEmpty(([Date].[Calendar].CurrentMember.PrevMember, [Measures].[Internet Sales Amount])),
([Date].[Calendar].CurrentMember.PrevMember, [Measures].[Prev non empty]),
([Date].[Calendar].CurrentMember.PrevMember, [Measures].[Internet Sales Amount])
), format_String = '$#,##0.00'
SELECT {[Measures].[Internet Sales Amount], [Measures].[Prev non empty]}
ON COLUMNS,
non empty
Descendants([Date].[Calendar].[Month].&[2007]&[12], [Date].[Calendar].[Date])
ON ROWS
FROM [Adventure Works]
WHERE [Customer].[Customer].&[12650]
You would have to replace the name of the date hierarchy, as well as the measure name from Internet Sales Amount to Quota in the recursive definition of the measure Prev non empty.
I'm trying to combine multiple members from a single hierarchy, though this leads to the following error:
Query (11, 3) The Jr-Kw-Mnd-Dag hierarchy is used more than once in the Crossjoin function.
This is a basic version of the Query I'm using:
SELECT
NON EMPTY {
[Measures].[Amount]
} ON COLUMNS
, NON EMPTY {
[Realisatiedatum].[Jr-Kw-Mnd-Dag].[Jaar]
* [Realisatiedatum].[Jr-Kw-Mnd-Dag].[Maand])
} ON ROWS
FROM
[Cube]
Jaar equals year in English, Maand equals month in English. This is what I'm trying to accomplish:
...
november 2013
december 2013
januari 2014
februari 2014
...
Last but not least, the hierarchy:
I would normally create several hierarchies within the Date dimension, such as Calendar, Financial and others that contain just financial year, calendar year, quarters etc.
If you have another hierarchy that contains the month, you could crossjoin with the year of the hierarchy you are using at the moment; then you won't be using the same hierarchy twice in the crossjoin function.
E.g.
, NON EMPTY {
( [Date Dimension].[Financial].[Financial Year]
* [Date Dimension].[Calendar].[Month] ) }
If you want to skip some levels in a user hierarchy, the best is to CrossJoin the corresponding attribute hierarchies of the remaining levels.
Here under I use the attribute hierarchy ([Geography].[City].[City], ..) instead of the user hierachy ([Geography].[Geography].[City],..) form the AW cube:
SELECT
[Measures].[Internet Sales Amount] ON 0,
[Geography].[State-Province].[State-Province] * [Geography].[City].[City] ON 1 FROM [Adventure Works]
Philip,
This issue is related to trying to pull multiple levels from the same hierarchy using a crossjoin and indeed you cannot. As mentioned in other replies, a good work around is to pull the columns you need from places other than the same hierarchy. But that works only if the cube design allows for it.
Your specific problem may be related to the tool you are using to develop the query and which levels in the hierarchy it returns.
For example, the following query when executed in SQL Server Management Studio (versions through V12.0.2000.8) returns only the City level of the hierarchy. But when executed from within design mode in the PowerPivot table import wizard it returns all levels in the hierarchy down to the city level including Country, State-Province and City.
select
[Measures].[Internet Order Count] on columns,
non empty [Customer].[Customer Geography].[City] on rows
from [Adventure Works]