MDX: add a condition to a measure - ssas

I would like to modify a measure in order to filter with a set: if the member belongs to the set, the measure returns the default measure, if not it returns 0.
Here is my attempt:
with
MEMBER spename AS [Dim misc].[SpePats].[Speciality Id].[{1037B3B2-EB0C-4DA3-BD9D-EB54F6CA92A5}].FirstChild.membervalue
SET spesSame AS filter([Dim misc].[SpePats].[Speciality Id].MEMBERS
,[Dim misc].[SpePats].CurrentMember.FirstChild.MemberValue = spename)--filter
MEMBER nb AS iif(IsEmpty(Intersect({[Dim misc].[SpePats].CurrentMember}
,spesSame))--intersect
,0
,Measures.[VParcours Nombre])
SELECT {nb} ON 0
,{[Dim misc].[SpePats].[Speciality Id].&[{1037B3B2-EB0C-4DA3-BD9D-EB54F6CA92A5}]
,[Dim misc].[SpePats].[Speciality Id].&[{1095C7D1-F24C-4E22-B2E4-4C2959E29FFD}]} ON 1
FROM [BDD PBM]
Here are some explanations:
First, the name of a characteristic of some hospital services is retrieved. For example, the name can be 'gynecology'.
Then, the set of speciality ids which name is the same as spename is created, hence spesSame is the list of speciality ids whose name is 'gynecology'.
What I need is to construct nb, defined by: if the currentmember of the hierarchy SpePats belongs to spesSame (intersect function), the result is the number of patients, otherwise it is 0, but the result is always the number of patients(in my request, in rows, the first Speciality id belongs to spesSame, not the second one).
thank you.

ok, the syntax of the iif was wrong: the correct one is:
MEMBER nb AS iif(Intersect({[Dim misc].[SpePats].CurrentMember}
,spesSame).count=0--intersect
,0
,[Measures].[VPARCOURS Nombre])
Apparently, isempty tests if the cell is empty. If someone can explains further the reason behind it...

Related

MDX where clause in subquery does not slice cube - how to understand?

This query gives me sales of one store:
select
[measures].[sales] on 0
from [MyCube]
where [store].[store].[042]
However, if I move the slicer to inside of the subquery, it gives me sales of all stores.
select
[measures].[sales] on 0
from (select
from [MyCube]
where [store].[store].[042]
)
How to understand the mechanisms behind this difference?
This is also noted in this article, but without much explanation.
----EDIT----:
I tried various things and read around for a while. I'd like to add a question: is there a scenario in which the where clause in sub-select does filter the result?
This query gives me sales of all stores in state MI (store [042] belongs to MI):
select
[measures].[sales] on 0
from (select
[store].[state].[MI] on 0
from [myCube]
where [store].[store].[042]
)
Thinking of 'inner query only filters if the filtered dimension is returned on an axis', the theory is proved wrong if I do this:
select
[measures].[sales] on 0
from (select
[store].[state].members on 0
from [myCube]
where [store].[store].[042]
)
The sub-select still returns one state MI, but the outer query returns sales of all stores (of all states).
----EDIT 4/13----:
Re-phrasing the question in AdventureWorks cube with screenshot.
Query 1: sales of one store
Query 2: it returns sales of all stores if where clause is in the sub-select.
Query 3: the two answers I got suggested that we select the dimension in an axis - here is the result - we get all cities.
select
[measures].[sales] on 0
from (select
from [MyCube]
where [store].[store].[042]
)
The above query reduces the scope of stores just to the member [042]. Make note that sub-select is executed before the actual select. So, when it comes to the select, the engine just sees a cube which has all the members in all the dimensions; but only the member [store].[store].[042] in the store dimension. It's as if the cube has been kept intact every where else but sliced off on the Store dimension.
If you go a step ahead and add the store on to one of the axes, like
select
[measures].[sales] on 0,
[store].[store].members on 1
from (select
from [MyCube]
where [store].[store].[042]
)
you would see that although the member [All] appears in the output, it actually is just comprised of only one store.
In essence, the [All] is a special member which is calculated with respect to scope of the cube. It reflects the combined effect of all the members in the cube.
In SQL terms, it is similar to:
select sales, store as [All] from
(select sales, store from tbl where store = '042') tbl
Even though you see Sales----All, it is but a reflection of sales for store [042]
Here are some other good references concerning sub-select and slicer debate:
http://bisherryli.com/2013/02/08/mdx-25-slicer-or-sub-cube/
https://cwebbbi.wordpress.com/2014/04/07/free-video-on-subselects-in-mdx/
Chris Webb's video being located here:
https://projectbotticelli.com/knowledge/what-is-a-subselect-mdx-video-tutorial?pk_campaign=tt2014cwb
This should still leave an All member:
SELECT
[measures].[sales] ON 0
FROM
(
SELECT
FROM [MyCube]
WHERE
[store].[store].[042]
);
...but the member [All] of the Store hierarchy will only now be made up of [store].[store].[042].
You can see this by adding the Store hierarchy onto ROWS:
SELECT
[measures].[sales] ON 0,
[store].MEMBERS ON 1
FROM
(
SELECT
FROM [MyCube]
WHERE
[store].[store].[042]
);
This is the AdvWorks version similar to the reference in your question:
SELECT
{[Measures].[Order Count]} ON 0
,[Subcategory].MEMBERS ON 1
FROM
(
SELECT
{
[Subcategory].[Subcategory].&[22]
} ON 0
FROM [Adventure Works]
);
It returns the member from the sub-select and the All member adjusted to take account of the subselect:
In the references article why is the [All] less than the sum of the other two - this is not down to the subselect but is in connection with the measure that he has chosen [Measures].[Order Count] which is a distinct count. If you take away the subselect you see exactly the same behaviour of the All member being less than the sum of the other subcategory members (I've marked the point at which the total of the parts becomes higher than the All member):
SELECT
{[Measures].[Order Count]} ON 0
,Order
(
[Subcategory].MEMBERS
,[Measures].[Order Count]
,bdesc
) ON 1
FROM [Adventure Works];
Order Count: on 1 order there might be several Product Subcategories - hence this behaviour.
Edit
This query of yours:
select
[measures].[sales] on 0
from (select
[store].[state].members on 0
from TestCube //<< added this!
where [store].[store].[042]
)
This inner script is not valid? Using the same dimension on an axes and the WHERE clause is not valid:
select
[store].[state].members on 0
from TestCube
where [store].[store].[042]
Edit2
An mdx script returns a cube, which may be sliced or not sliced, but nevertheless it returns a cube. The WHERE clause is used to slice the cube that is returned. If we were using a third party tool then the dimension added to the WHERE clause would go into a combobox - with say Cliffside selected. BUT the user could effectively select Ballard from that combobox - it is just a slicer. The WHERE clause is not changing the cube that is returned by the mdx script, it is just affecting what is displayed in the cellset.
WHERE is valid within a subselect. It is part of the definition:
https://msdn.microsoft.com/en-us/library/ff487138.aspx
I've never found a use case for a subselect's WHERE clause.
Edit3
This link will explain things:
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/ccb66ac3-0f9a-4261-8ccc-b6ecc51b6f07/is-where-clause-pointless-inside-a-subselect?forum=sqlanalysisservices
As Darren gosbell says in the answer to this question:
https://msdn.microsoft.com/en-us/library/ff487138.aspx it says that:
The WHERE clause does not filter the subspace.

The MDX function CURRENTMEMBER failed because the coordinate for the 'Date' attribute contains a set

I'm trying to run this query:
select [Dim Date].[Date] on ROWS,
{[Measures].[Available Time Net],[Measures].[Logged On Time Net]} on columns
from [OTS Agent Time Net Data]
where {[Dim Date].[Date].&[08/01/2014]:[Dim Date].[Date].&[12/31/2014]}
I want to get the measures that exist in the where clause, but I also want to show the dates on the rows. I keep getting an error.
Why not just add the set directly to the ROWS ?
SELECT
NON EMPTY
{
[Dim Date].[Date].&[08/01/2014] : [Dim Date].[Date].&[12/31/2014]
} ON ROWS
,{
[Measures].[Available Time Net]
,[Measures].[Logged On Time Net]
} ON COLUMNS
FROM [OTS Agent Time Net Data];
You cannot use the same hierarchy ([Dim Date].[Date]) both on one axis and in the slicer (MDX tutorial); I guess using a sub-query would be fine for your example:
select
[Dim Date].[Date] on ROWS,
{[Measures].[Available Time Net],[Measures].[Logged On Time Net]} on columns
from (
select {[Dim Date].[Date].&[08/01/2014]:[Dim Date].[Date].&[12/31/2014]} on 0
from [OTS Agent Time Net Data]
)
Hope that helps.
(edit: using a named-set or using the slicer content right in the axis - see other responses - is a bit different because the request is filtering on a range of days but select [Dim Date].[Date] does not necessarily displays days; that could for example be the 'All' of the hierarchy of [Dim Date].[Date])
I agree with Marc's answer and that's probably the best way to do it. But there is one more way to do it, which doesn't need subselect, by making use of named sets.
with set DateRange AS
{[Dim Date].[Date].&[08/01/2014]:[Dim Date].[Date].&[12/31/2014]}
select NON EMPTY DateRange on ROWS,
{[Measures].[Available Time Net],[Measures].[Logged On Time Net]} on COLUMNS
from [OTS Agent Time Net Data]
EDIT
As per your request, if you want to additionally filter by time, here's one way to proceed. Replace the defenition of set DateRange with the below:
with set DateRange AS
EXISTS({[Dim Date].[Date].&[08/01/2014]:[Dim Date].[Date].&[12/31/2014]}
,[Dim Time].[Time].&[1930]
, "Time Net"
)
Few things which you need to fill in here..
Assuming time dimension's hierarchy to be [Dim Time].[Time]. Please replace with the actual name along with the member's format(Assumed it to be &[1930]
Also, Time Net is the measure group name to which the measures [Measures].[Available Time Net] and [Measures].[Logged On Time Net] belong to. Again, replace with original name.

Non-empty previous value - MDX

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.

Calculating percentile values in SSAS

I am trying to calculate percentile (for example 90th percentile point of my measure) in a cube and I think I am almost there. The problem I am facing is, I am able to return the row number of the 90th percentile, but do not know how to get my measure.
With
Member [Measures].[cnt] as
Count(NonEmpty(
-- dimensions to find percentile on (the same should be repeated again
[Calendar].[Hierarchy].members *
[Region Dim].[Region].members *
[Product Dim].[Product].members
,
-- add the measure to group
[Measures].[Profit]))
-- define percentile
Member [Measures].[Percentile] as 90
Member [Measures].[PercentileInt] as Int((([Measures].[cnt]) * [Measures].[Percentile]) / 100)
**-- this part finds the tuple from the set based on the index of the percentile point and I am using the item(index) to get the necessary info from tuple and I am unable to get the measure part
Member [Measures].[PercentileLo] as
(
Order(
NonEmpty(
[Calendar].[Hierarchy].members *
[Region Dim].[Region].members *
[Product Dim].[Product].members,
[Measures].[Profit]),
[Measures].[Profit].Value, BDESC)).Item([Measures].[PercentileInt]).Item(3)**
select
{
[Measures].[cnt],
[Measures].[Percentile],[Measures].[PercentileInt],
[Measures].[PercentileLo],
[Measures].[Profit]
}
on 0
from
[TestData]
I think there must a way to get measure of a tuple found through index of a set. Please help, let me know if you need any more information. Thanks!
You should extract the tuple at position [Measures].[PercentileInt] from your set and add the measure to it to build a tuple of four elements. Then you want to return its value as the measure PercentileLo, i. e. define
Member [Measures].[PercentileLo] as
(
[Measures].[Profit],
Order(
NonEmpty(
[Calendar].[Hierarchy].members *
[Region Dim].[Region].members *
[Product Dim].[Product].members,
[Measures].[Profit]),
[Measures].[Profit], BDESC)).Item([Measures].[PercentileInt])
)
The way you implemented it, you tried to extract the fourth (as Item() starts counting from zero) item from a tuple containing only three elements. Your ordered set only has three hierarchies.
Just another unrelated remark: I think you should avoid using complete hierarchies for [Calendar].[Hierarchy].members, [Region Dim].[Region].members, and [Product Dim].[Product].members. Your code looks like you are including all levels (including the all member) in the calculation. But I do not know the structure and names of your cube, hence I may be wrong with this.
An alternate method could be to find the median of the last 20% of the records in the table. I've used this combination of functions to find the 75th percentile. By dividing the record count by 5, you can use the TopCount function to return a set of tuples that make up 20% of the whole table sorted in descending order by your target measure. The median function should then land you at the correct 90th percentile value without having to find the record's coordinates. In my own use, I use the same measure for the last parameter in both the Median and TopCount functions.
Here's my code:
WITH MEMBER Measures.[90th Percentile] AS MEDIAN(
TOPCOUNT(
[set definition]
,Measures.[Fact Table Record Count] / 5
,Measures.[Value by which to sort the set so the first 20% of records are chosen]
)
,Measures.[Value from which the median should be determined]
)
Based on what you've supplied in your problem definition, I would expect your code to look something like this:
WITH MEMBER Measures.[90th Percentile] AS MEDIAN(
TOPCOUNT(
{
[Calendar].[Hierarchy].members *
[Region Dim].[Region].members *
[Product Dim].[Product].members
}
,Measures.[Fact Table Record Count] / 5
,[Measures].[Profit]
)
,[Measures].[Profit]
)

Getting a count of users each day in Mondrian MDX

I'm trying to write a query to give me the total number of users for each customer per day.
Here is what I have so far, which for each customer/day combination is giving the total number of user dimension entries without splitting them up by customer/day.
WITH MEMBER [Measures].[MyUserCount]
AS COUNT(Descendants([User].CurrentMember, [User].[User Name]), INCLUDEEMPTY)
SELECT
NON EMPTY CrossJoin([Date].[Date].Members, [Customer].[Customer Name].Members) ON ROWS,
{[Measures].[MyUserCount]} on COLUMNS
FROM
[Users]
The problem with your calculated member is that [User].CurrentMember is set to the All member for every row tuple, and thus the count is the total. What you need is a way for the [Customer].CurrentMember and [Date].CurrentMember to effectively filter the [User] dimension.
You need to use a measure that makes sense, i.e. that will have a non-empty value for meaningful joins of the dimension members that you're interested in.
To find this out, you could start by running a query like this:
SELECT
NON EMPTY CrossJoin(
[User].[User Name].Members,
[Measures].[Some measuse]
) ON COLUMNS,
NON EMPTY CrossJoin(
[Date].[Date].Members,
[Customer].[Customer Name].Members
) ON ROWS
FROM [Project]
You would have selected Some measure adequately. The results of that query will be a lot of empty cells, but in a given row, the columns that do have a value correspond to the Users that are related to a given Customer x Date tuple (on the row). You want to count those columns for every row. COUNT and FILTER are what you need, then the query with the calculated member will be
WITH MEMBER [Measures].[User count] AS
COUNT(
FILTER(
[User].[User Name].Members,
NOT ISEMPTY([Measures].[Some measure])
)
)
SELECT
NON EMPTY {[Measures].[User count]} ON COLUMNS,
NON EMPTY CrossJoin(
[Date].[Date].Members,
[Customer].[Customer Name].Members
) ON ROWS
FROM [Users]
I am assuming a fair bit here, but with some experimentation you should be able to work it out.