Get next member that matches criteria mdx - ssas

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.

Related

MDX Result Count

I am a beginner in MDX queries. Can any one tell me how to get the record count that is a result of a MDX query?
The query is following:
select {[Measures].[Employee Department History Count],[Measures].[Rate]} on columns, Non Empty{{Filter([Shift].[Shift ID].[Shift ID].Members, ([Shift].[Shift ID].CurrentMember.name <> "1"))}*{[Employee].[Business Entity ID].[Business Entity ID].Members}} on rows from [Adventure Works2012].
I have tried various methods and I haven't really got a solution for that.
I assume you mean row count when you talk of "record count", as MDX does not know a concept of records, but the result shown from an MDX query is the space built by the tuples on the axes.
I see two possibilities to get the row count:
Just count the rows returned from your above query in the tool from which you call the MDX query.
If you want to count in MDX, then let's state what you want to have:
You want to know the number of members of the set of non empty combinations of [Shift ID]s and [Business Entity ID]s where the Shift ID is not "1" and at least one of the measures [Employee Department History Count] and [Rate] is not null.
To state that different: Let's call the tuples like above for which the first measure is not null "SET1", and the tuples like above for which teh second measure is not null "SET2". Then you you want to know the count of the the tuples which are contained in one of these sets (or in both).
To achieve this, we define these two sets and then a calculated menber (a new measure in our case) containing this calculation in its definition, and then use this calculated member in the select clause to show it:
WITH
SET SET1 AS
NonEmpty({{Filter([Shift].[Shift ID].[Shift ID].Members,
([Shift].[Shift ID].CurrentMember.name <> "1"))}
* {[Employee].[Business Entity ID].[Business Entity ID].Members}},
{[Measures].[Employee Department History Count])
SET SET2 AS
NonEmpty({{Filter([Shift].[Shift ID].[Shift ID].Members,
([Shift].[Shift ID].CurrentMember.name <> "1"))}
* {[Employee].[Business Entity ID].[Business Entity ID].Members}},
{[Measures].[Rate])
MEMBER [Measures].[MyCalculation] AS
COUNT(SET1 + SET 2)
SELECT [Measures].[MyCalculation] ON COLUMNS
FROM [Adventure Works2012]
Please note:
The sets SET1 and SET2 are not absolutely necessary, you could also put the whole calculation in one long and complicated definition of the MyCalculation measure, but splitting it up makes is easier to read. However, the definition of a new member is necessary, as in MDX you can only put members on axes (rows, columns, ...). These members can either already been defined in the cube, or you have to define them in the WITH clause of your query. There is no such thing as putting expressions/calculations on axes in MDX, only members.
The + for sets is a union which removes duplicates, hence this operation gives us the tuples which have an non empty value for at least one of the measures. Alternatively, you could have used the Union function equivalently to the +.
The Nonempty() I used in the definitions of the sets is the NonEmpty function, which is slightly different from the NON EMPTY keyword that you can use on the axes. We use one of the measures as second argument to this function in both set definitions.
I have currently no working SSAS installation available to test my statement, hence there might be a minor error or typo in my above statement, but the idea should work.

MDX/SSRS - how do you get the “ALL” label to display in a report then add additional dimensions

I recently posted a question on how to get the ALL label to appear in a SSRS report. "whytheq" was kind enough to post an example that works. Below is the MDX with an added dimension that if I uncomment it will give me an error:
Query (8, 5) Two sets specified in the function have different dimensionality.
WITH
MEMBER [Due Date].[Calendar Month].[All].[YTD] AS
[Due Date].[Calendar Month].[All]
SELECT
NON EMPTY
{[Measures].[Freight]} ON COLUMNS
,NON EMPTY
{
//[Product].[Color].[Color].MEMBERS*
[Due Date].[Calendar Month].[All].[YTD],
[Due Date].[Calendar Month].[Calendar Month].MEMBERS
} ON ROWS
FROM [Adventure Works Cube];
I get that I have a Dimension at the .MEMBERS level and I've also added the .[YTD] dimension.
I did noted that if I comment out the .[YTD] and uncomment the [Product] dimension this works but I have to use an * instead of a comma like so.
[Product].[Color].[Color].MEMBERS*
//[Due Date].[Calendar Month].[All].[YTD],
[Due Date].[Calendar Month].[Calendar Month].MEMBERS
Is it possible to have the Product dimension include in the rows along with the YTD and Calendar Month.MEMEBERS?
Greg's answer to your question is fine, I just wanted to show you the nuts and bolts of your query so maybe you get a better understanding of how you use an MDX query and a cube's structure to produce the result you want.
First, here's the basic structure of most MDX queries:
SELECT
Set_Expression ON COLUMNS
,
Set_Expression ON ROWS
FROM CUBE;
(There's a bit more to MDX, but this is probably 80-90%.)
A set expression is an expression that returns a set - so as you can see it's pretty important that understand what a set is and how to build a valid expression that returns the right set.
So first, what is a set, again? A set is -drum roll- .... a set of tuples1. But not just any tuples - only tuples which have the same dimensional hierarchical structure (also called dimensionality). Let's look at some examples of potential set expressions, and I'll tell you if they're valid or not.
A set with one tuple? VALID.
{ Product.Product.Laptop }
A set with two tuples from the same dimension hierarchy? VALID.
{ Product.Product.Laptop , Product.Product.Desktop }
A set with two tuples from the same dimension hierarchy but different levels? VALID.
{ Date.DateHierarchy.Year.2008 , Date.DateHierarchy.Quarter.2009Q1 }
A set with two tuples from the same dimension, but a different hierarchy? INVALID.
{ Product.Product.[Laptop] , Product.Category.[Hardware] }
Why is this invalid? Because the two tuples have different dimensionalities. If you consider back to the literal metaphor of a "cube", each dimension hierarchy is a "face" and each tuple is a slice of the cube along the faces that make up its dimensionality (and any dimension hierarchy that isn't part of the tuple's explicit dimensionality is treated as an implicit "All members" at execution time) So in order to combine two or more tuples into a set - so you can extract them from the cube in a single slice - they must all come from the same set of faces - the same dimensionality.
A set with a tuple and a set function (an MDX function which returns a set of tuples) from the same dimensional hierarchy? VALID.
{
Tail ( Order ( Product.Product.Members, Measures.Profit, BASC ), 5),
Product.Product.All
}
A set with two set functions from the same dimensional hierarchy? VALID
{ Subset(Product.Product.Members,0,5), Subset(Product.Product,Members,6,5) }
A set with a tuple with two dimensions? VALID but! you have to put parentheses around the tuple. (You can put parentheses around tuples with just one dimension, but it's not necessary.)
{ ( Product.Product.Laptop, Date.Year.2015 ) }
A set with two tuples each with two dimensions at the same respective hiearchical levels? VALID
{
( Product.Product.Laptop, Date.Year.2015 ),
( Product.Product.Tablet, Date.Year.2013 )
}
(Note the members don't have to be the same at all, just the hierarchies. Hopefully this is the point where you recognize what "dimensionality" means.)
A set with two tuples each with two dimensions at the same respective hiearchical levels but not in the same order within the tuples? INVALID
{
( Product.Product.Laptop, Date.Year.2015 ),
( Date.Year.2013, Product.Product.Tablet )
}
(Compare to UNION in SQL - you have to have consistent ordering so MDX can build the set up to perform other tasks, such as nesting each member of a tuple correctly on a query axis.)
And finally, if we want to combine two sets from different dimensional hierarchies into a single set, we use the handy dandy CrossJoin function:
CrossJoin ( { Product.Product.Laptop } , Date.Year.Members )
As you've discovered, you can also use an asterisk (*) to perform a cross join of two sets
Product.Product.Laptop * Date.Year.Members
Now it's important to understand what's going on here. I said "combine sets" earlier, but as you've seen with all the examples, you can't just throw various sets of tuples with different dimensionality together into a single set.
So what's a CrossJoin doing?
Well, given a CrossJoin of
{Set With Tuple Dimensionality (A)} * {Set with Tuple Dimensionality (B)}
The resulting set has a tuple dimensionality of (A, B).
So looking back at our CrossJoin example, our left-hand side has just one tuple, and its dimensionality is (Product.Product). And our right-hand side has many tuples (every member of the Date.Year hierarchy) with the dimensionality of (Date.Year). So our final set of tuples will have the dimensionality of (Product.Product, Date.Year).
So say we had every year from 2010-2015 in our cube, our final set would be 6 tuples of
{
( Product.Product.Laptop, Date.Year.2010),
( Product.Product.Laptop, Date.Year.2011),
( Product.Product.Laptop, Date.Year.2012),
( Product.Product.Laptop, Date.Year.2013),
( Product.Product.Laptop, Date.Year.2014),
( Product.Product.Laptop, Date.Year.2015)
}
So you could in theory tack on more tuples to this set as long as they're simpatico with the same dimensionality - say, (Product.Product.Tablet, Date.Year.2013)
So this would also be a VALID set expression:
{
Product.Product.Laptop * Date.Year.Members,
(Product.Product.Tablet, Date.Year.2013)`
}
So now going back to your problem, if we uncomment your broken line of code we see you've got
{
[Product].[Color].[Color].MEMBERS *
[Due Date].[Calendar Month].[All].[YTD],
[Due Date].[Calendar Month].[Calendar Month].MEMBERS
}
So, here you've got a set of tuples
[Product].[Color].[Color].MEMBERS *
[Due Date].[Calendar Month].[All].[YTD]
whose tuples have the dimensionality of
(Product.Color, Due Date.Calendar Month)
And then you're trying to tack on another set of tuples to this set
[Due Date].[Calendar Month].[Calendar Month].MEMBERS
whose tuples have a dimensionality of
(Due Date.Calendar Month)
Now do you understand the error message you got?
And this leads me to why I wrote this very long post at 1 in the morning: your error is an error of fundamentally misunderstanding the concept of a set in MDX, not simply an error of syntax.
Obviously you want your final set's tuple's dimensional hierarchy structure to be
(Product.Color, Due Date.Calendar Month)
And again, Greg's answer achieves this. So really I hope my post has explained how Greg's answer achieves this!
Also, here's two alternate syntactical ways to achieve the same set result you want on the COLUMNS axis:
NON EMPTY { ([Product].[Color].[Color].Members, [Due Date].[Calendar Month].Members ) }
and
NONEMPTY ( [Product].[Color].[Color].Members, [Due Date].[Calendar Month].Members )
Basically, the (set, set) syntax also performs a crossjoin, as does the NONEMPTY(set1, set2) function. Just so you can see that there are multiple syntactical ways to achieve the same concept.
Quick refresher on tuples: Each cell in your cube represents a unique set of dimension hierarchy members. A tuple is a set of dimension hierarchy members which constitute a set of cells (or coordinate space) in your cube. (N.B.: Any dimension hierarchy not explicitly named in a tuple expression uses its default member, which if not explicitly set in an SSAS cube model is the All member.)
So the tuple expression (Product.Product.Laptop) points to the cellset in the cube where the member of the Product.Product dimension hierarchy is Product.Product.Laptop, and for all the other dimension hierarchies the member is the default member (if it's the All member, the cube basically doesn't do any work, since no slicing is required on that dimension hierarchy.)
In the image below, you can see how the tuple expression (Time.[2nd half], Source.nonground.air) is applied to the cube to produce its coordinate space.
Okay, back to the top.
Try this:
NON EMPTY
{
[Product].[Color].[Color].MEMBERS*
{
[Due Date].[Calendar Month].[All].[YTD],
[Due Date].[Calendar Month].[Calendar Month].MEMBERS
}
} ON ROWS

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.

MDX: using distinct count in YTD for calculated member

I have created new measure that counts distinct policies (this measure is called FK Policy Distinct Count).
Then I created new calculated member called CountPolicyEndorsesNull which counts all policies from FK Policy Distinct Count using a filter:
(([Policy].[Endorses].&[0],[FK Policy Distinct Count]).
Than I did new calculated member called CountPolicy:
SUM(EXCEPT([Policy].[Policy Status].[Policy Status],[Policy].[Policy Status].&[Void]), [Measures].[CountPolicyEndorsesNull])
Next, I created a new member CountNewBound
SUM(
{
[Submission].[Tran Type].&[New], [Submission].[Tran Type].&[Developed]
},
[Measures].[CountPolicy]
)
And finally, YTDCountNewBound
SUM(YTD([Invoice Date].[Date Hierarchy].CurrentMember), [Measures].[CountNewBound])
Obviously, SUM function doesn't work in this case. Any idea how to make proper YTD count for calculated member?
Distinct count is a special measure which should be managed with a little more care. The rational behind this is that the when evaluating the measure a set of previous values is kept in memory. In order to improve performance, this structure is not passed and it's quickly converted to a scalar value.
Going back to your problem :
Distinct count can be evaluated over a tuple without problem, but you'll get in problems once you try to evaluate over a set of tuples. A possible, but costly and not always possible, is to create a hierarchy of values so you can convert your set in a member of a dimension.
In your case instead of using YTD([Invoice Date].[Date Hierarchy].CurrentMember) function using another hierarchy -> [Invoice Date].[YTD Date Hierarchy].
All this depends on the specific OLAP implementation you're using, but I guess holds true for mainly all OLAP vendors.