how to avoid the error "Category member XXX defined as an empty set" in icCube? - mdx

I've got the following MDX code to create a category dimension with 4 members:
all members that make up of 0-50% of the measure (TopPercent)
members 50-80%
members 80-95%
members 95-100%
The code works perfectly on levels with a lot of members:
WITH
MEMBER [measures].[v] as eval([Tijd].[Tijd].[jaar].[2018],[Measures].[Bedrag])
set [selection] as Order( nonempty([Categorie].[Categorie].[categorie].members,[measures].[v]), [measures].[v], BDESC)
CATEGORY HIERARCHY [Stats].[ABCD], DEFAULT_MEMBER_NAME = "Totaal", LEVEL_NAME_PATTERN="L - ABCD - ${levelDepth}"
CATEGORY MEMBER [Stats].[ABCD].[Totaal].[A (0-50%)] as
TopPercent([selection],50, [measures].[v] ), ADD_CHILDREN=true
CATEGORY MEMBER [Stats].[ABCD].[Totaal].[B (50-80%)] as
TopPercent([selection],80, [measures].[v] )
- TopPercent([selection],50, [measures].[v]), ADD_CHILDREN=true
CATEGORY MEMBER [Stats].[ABCD].[Totaal].[C (80-95%)] as
TopPercent([selection],95, [measures].[v])
- TopPercent([selection],80, [measures].[v] ), ADD_CHILDREN=true
CATEGORY MEMBER [Stats].[ABCD].[Totaal].[D (95-100%)] as
Order([selection], [measures].[v], BDESC)
- TopPercent([selection],95, [measures].[v]), ADD_CHILDREN=true
SELECT
// Measures
{[measures].[v]} On 0,
// Columns
[Stats].[ABCD].[L - ABCD - 1].members on 1,
// Rows
[Stats].[ABCD].[L - ABCD - 2].members on 2
FROM (select [Tijd].[jaar].[2018] on 0 from [Spendzoom])
/*ic3navigation*/
But when I run the MDX code with:
set [selection] as Order( nonempty([Categorie].[Categorie].[type].members,[measures].[v]), [measures].[v], BDESC)
I get the error: Category member "[Stats].[ABCD].[Totaal].[C (80-95%)]'defined as an empty set.
I have tried to rewrite the definitions, as:
subcubeminus(TopPercent .... , TopPercent)
But that gave completely strange results.
How can I overcome this error and have a generic approach that allways works regardless of the contents of the hierarchy & level in the [selection] definition?

To understand what happening you should check how TopPercent works (it's not really whay you expected).
Try this MDX :
WITH
MEMBER [measures].[v] as eval([Tijd].[Tijd].[jaar].[2018],[Measures].[Bedrag])
STATIC SET [selection] as [Categorie].[Categorie].[categorie].members
SELECT
[measures].[v] On 0,
TopPercent([selection],95, [measures].[v]) on 1,
TopPercent([selection],80, [measures].[v] ) on 2
FROM
(select [Tijd].[jaar].[2018] on 0 from [Spendzoom])
As you see both return the same set, and that is not what you are looking I guess.

As ic3 mentioned in the comments, as of icCube 6.8.10, icCube allows now to have empty categories. Y
For me this means, business wise, that regardless of the global filter settings the categories ALWAYS work. In case it is an empty set, it results a blank value in the dashboards.
example of a Parato analysis for just 1 vendor (bedrag = amount, #Fact = nr of invoices, #Lev = nr of suppliers)
Exactly as desired.

Related

SSAS: How can I create calculated member using multiple conditions on different hierarchy

I have problem with my SSAS Cube.
I have to implement basic calculation to my Cube:
sum(Ammount) where BOOK = "Assets" and CD_PRODUCT_L4 <> "LoanLoss"
My dimmension is in image below:
Dimension attributes: BOOK, CD_PRODUCT_L4, CD_PRODUCT_L5, ..
Hierarchy PROD: CD_PRODUCT_L4 - CD_PRODUCT_L5
CREATE MEMBER CURRENTCUBE.[Measures].[Principal Loans]
AS (
except(
[PLV PRH HDIM CB].[BOOK].&[ASSETS_ON],
[PLV PRH HDIM CB].[COD PRODUCT L4].&[51L4]
),
[Measures].[EOM PRINCIPAL_a])
The biggest thing is that conditions are not from same hierarchy level so I can't use except and I know no other way to implement this particular condition set
Please HELP, Thank you
Product dimmension model
I manage to solve the issue by adding hierarchy to my dimension which allowed me to use except as I wanted before but I'am not satisfied with this solution because it looks more like workaround then solution
CREATE MEMBER CURRENTCUBE.[Measures].[Principal Loans] AS
sum((except([PLV PRH HDIM CB].[Hierarchy BOOK].[BOOK].&[ASSETS_ON].children,[PLV PRH HDIM CB].[Hierarchy BOOK].[COD PRODUCT L4].&[51L4]),[Measures].[EOM PRINCIPAL_a]))
This is probably the closest example that I can provide you to what you need.
This, code below, is just one of about 50 or so Measures we have defined, ranging from Counts, Averages, Sums, Percentages, etc. We take a single DW value and perform at least 2 seperation calculations, most have 4 or more different calculations, from that one field.
CREATE MEMBER CURRENTCUBE.[Measures].[Count Previous Difference]
AS Case
// Test for current coordinate being on (All) member.
When [Date].[YearMonth].CurrentMember.Level Is [Date].[YearMonth].[(All)]
Then "NA"
Else
( [Date].[YearMonth].CurrentMember, [Measures].[Gross Count] )
-
( ParallelPeriod (
[Date].[YearMonth].CurrentMember.Level,
1,
[Date].[YearMonth].CurrentMember
),
[Measures].[Gross Count]
)
End,
FORMAT_STRING = "#,##0.00;-#,##0.00",
NON_EMPTY_BEHAVIOR = { [Gross Count] },
VISIBLE = 0 , ASSOCIATED_MEASURE_GROUP = 'Some Summary';
Keep in mind we also use our DW & Cube with SharePoint and SSRS reports.

UNION multiple MDX queries in SSAS (powerpivot)

I have some sort of difficulties trying to join 2 MDX queries together. When running them separately they work fine. The script below
WITH
MEMBER [Measures].[ParameterCaption] AS
[Main_Incidents].[Priority].CurrentMember.Member_Caption
MEMBER [Measures].[ParameterValue] AS
[Main_Incidents].[Priority].CurrentMember.UniqueName
MEMBER [Measures].[ParameterLevel] AS
[Main_Incidents].[Priority].CurrentMember.Level.Ordinal
SELECT
{
[Measures].[ParameterCaption]
,[Measures].[# Incidents]
,[Measures].[%SLA]
} ON COLUMNS
,[Main_Incidents].[Priority].ALLMEMBERS ON ROWS
FROM [Model];
WITH
MEMBER [Measures].[ParameterCaption] AS
[Main_Incidents].[usr_directorate].CurrentMember.Member_Caption
MEMBER [Measures].[ParameterValue] AS
[Main_Incidents].[usr_directorate].CurrentMember.UniqueName
MEMBER [Measures].[ParameterLevel] AS
[Main_Incidents].[usr_directorate].CurrentMember.Level.Ordinal
SELECT
{
[Measures].[ParameterCaption]
,[Measures].[# Incidents]
,[Measures].[%SLA]
} ON COLUMNS
,[Main_Incidents].[usr_directorate].ALLMEMBERS ON ROWS
FROM [Model];
The most important bit for me is that I need the label column to show. So I want to UNION the 2 queries together so that the ParameterCaption captures values from "Priority" dimension and "Directorate" dimension....
Please someone help me to achieve this?
This is a bit complex, but definitely possible.
Union in MDX only works for members of the same hierarchy, so to achieve this we need to make the row members into Tuples that combine the two hierarchies. We can do this by cross joining each of the ALLMEMBERS sets to the [All] member for the other hierarchy. Then we just need to change the Parameter Caption, Value and Level to conditionally get the value from the appropriate hierarchy.
This could look something like the code below:
WITH
MEMBER [Measures].[ParameterCaption] AS
IIF([Main_Incidents].[Priority].CurrentMember.Level.Ordinal = 0, [Main_Incidents].[usr_directorate].CurrentMember.Member_Caption, [Main_Incidents].[Priority].CurrentMember.Member_Caption)
MEMBER [Measures].[ParameterValue] AS
IIF([Main_Incidents].[Priority].CurrentMember.Level.Ordinal = 0, [Main_Incidents].[usr_directorate].CurrentMember.UniqueName, [Main_Incidents].[Priority].CurrentMember.UniqueName)
MEMBER [Measures].[ParameterLevel] AS
IIF([Main_Incidents].[Priority].CurrentMember.Level.Ordinal = 0, [Main_Incidents].[usr_directorate].CurrentMember.Level.Ordinal , [Main_Incidents].[Priority].CurrentMember.Level.Ordinal)
SELECT
{
[Measures].[ParameterCaption]
,[Measures].[# Incidents]
,[Measures].[%SLA]
} ON COLUMNS
,{
[Main_Incidents].[Priority].ALLMEMBERS * [Main_Incidents].[usr_directorate].[All],
[Main_Incidents].[Priority].[All] * [Main_Incidents].[usr_directorate].ALLMEMBERS
} ON ROWS
FROM [Model];

MDX query - best salesmen who sold all of given products

Let's say I have two simple dimensions:
Products - with id and name
Salesmen - with id and name
My fact table is named SALES and contains the ids of the abovementioned.
I need to produce a query that will show the names of salesmen who sold all of the given products.
This code solves the problem for two items X and Y:
SELECT
{} on 0,
EXISTS(
EXISTS(
{[Salesmen].[Name].MEMBERS},
{[Products].[Name].&[X]}
)
,{[Products].[Name].&[Y]}
)
ON 1
FROM [Test];
The other version is:
SELECT
{} on 0,
INTERSECT(
NONEMPTY(
{[Salesmen].[Name].MEMBERS}
,([Products].[Name].&[X])
)
,NONEMPTY(
{[Salesmen].[Name].MEMBERS}
,([Products].[Name].&[Y])
)
)
ON 1
FROM [Test];
However, this method becomes troublesome if the list of given products is large, for example - 100 random products..
Do you have a property member_key for the hierarchy [Products].[Name] ? We can test like this:
WITH
MEMBER [Measures].[Meas1] AS
[Products].[Name].CurrentMember.PROPERTIES("KEY ID")
MEMBER [Measures].[Meas2] AS
[Products].[Name].CurrentMember.MEMBER_Key
MEMBER [Measures].[Meas3] AS
[Products].[Name].CurrentMember.MEMBERvalue
select
{
[Measures].[Meas1]
,[Measures].[Meas2]
,[Measures].[Meas3]
} on COLUMNS,
[Products].[Name].MEMBERS on ROWS
FROM [Test];
Hopefully one of the custom measures gives you a value? I'll assume Meas2 is working (swap to a different one if Meas1 or Meas3 is returning numbers)
WITH
MEMBER [Measures].[Meas2] AS
[Products].[Name].CurrentMember.MEMBER_Key
SET [ProdsetA] AS
FILTER(
[Products].[Name].MEMBERS
,[Measures].[Meas2] <100
)
SET [ProdsetB] AS
FILTER(
[Products].[Name].MEMBERS
,[Measures].[Meas2] >500
)
SELECT
{} on 0,
INTERSECT(
NONEMPTY(
{[Salesmen].[Name].MEMBERS}
,[ProdsetA]
)
,NONEMPTY(
{[Salesmen].[Name].MEMBERS}
,[ProdsetB]
)
)
ON 1
FROM [Test];
... the >100 and <500 are important. These are the criteria for the filter function to use. The custom set [ProdsetA] will only contain Products that have MEMBER_Key that are <100 whereas the custom set [ProdsetB] will only contain Products that have MEMBER_Key that are >500. You need to use the member values presented to you by the first script to decide what values 100 and 500 should be in your cube context (...I don't know the key values in your cube so just used 100 and 500 as placeholders)

MDX - icCube - How to get a DYNAMIC topcount/toppercent with other aggregated?

Using the following mdx, I'm able to retrieve correct data dynamically.
create CATEGORY MEMBER [Stats].[Top].[All Etabs].[Top 5 Etablissements]
as topcount( [Etablissement].[Etablissement].[Etablissement].allMEMBERS, 5, [Measures].[Nbsejours]),ADD_CHILDREN=true
create CATEGORY MEMBER [Stats].[Top].[All Etabs].[Autres Etablissements (>5)]
as Except([Etablissement].[Etablissement].[Etablissement].members, TopCount( [Etablissement].[Etablissement].[Etablissement].MEMBERS, 5, [Measures].[Nbsejours])),ADD_CHILDREN=false
create dynamic set [Top 5 & Others]
as {[Stats].[Top].[Top 5 Etablissements], [Stats].[Top].[Autres Etablissements (>5)]}
Select {[Measures].[NbSejours]} on 0,
nonempty ([Top 5 & Others]) on 1
From //[Cube]
( SELECT { {[Geographique].[Zone].[All-M].&[1006]} } ON 0 FROM [Cube])
But, the topCount is not dynamic itself. In this example, the top 5 etablissement never change, only the values do change...
Is there a way to get this with dynamic topCount/topPercent ?
Txs,
Bertrand.
Categories (*) have not yet the dynamic flag so it's not possible to define a category that will be calculated for each MDX request once as it happens for a set.
So it's going to be something more like (note I've use the SubCubeComplement that is a lot - may be really a lot - faster )
create dynamic set [Top 5] as
topcount( [Etablissement].[Etablissement].[Etablissement].members, 5, [Measures].[Nbsejours])
*** End script ***
WITH
CATEGORY HIERARCHY [Stats].[Top], DEFAULT_MEMBER_NAME = "All Etabs"
CATEGORY MEMBER [Stats].[Top].[All Etabs].[Top 5 Etablissements] as
[Top 5],ADD_CHILDREN=true
CATEGORY MEMBER [Stats].[Top].[All Etabs].[Autres Etablissements (>5)] as
SubCubeComplement([Top 5]),ADD_CHILDREN=false
SELECT
{[Measures].[NbSejours]} on 0,
{ [Stats].[Top].[Top 5 Etablissements],
[Stats].[Top].[Autres Etablissements (>5)] } on 1
From [Cube]
(*) For the people that are not used to icCube, Categories is a way defining a 'new' member as a set of members (they might have different dimensionalities). This ensures for complex calculations, schemas with many-to-many relations that the values are correctly calculated. Otherwise it might be a little nightmare to ensure calculation correctness.

Excluding (All) from sets

I'm using the following but i think there's probably a much simpler method of excluding the All members from the results?
WITH
SET [Non_All_Distributors] AS
{FILTER(
[Distributor Name].members,
(InStr(1, [Distributor Name].CurrentMember.NAME, "All") = 0)
)}
SET [Non_All_Countries] AS
{FILTER(
[Geography Country].members,
(InStr(1, [Geography Country].CurrentMember.NAME, "All") = 0)
)}
SELECT
NON EMPTY
[Dimension].[Hierarchy].DEFAULTMEMBER
ON COLUMNS,
NON EMPTY
[Non_All_Distributors]
*
[Non_All_Countries]
*
Tail([Date].[Date - Calendar Month].[Calendar Day].Members,60)
*
{
[Measures].[Revenue],
[Measures].[NumClients]
}
ON ROWS
FROM [OURCUBE]
Just use
SELECT
NON EMPTY
[Dimension].[Hierarchy].DEFAULTMEMBER
ON COLUMNS,
NON EMPTY
[dimension of Distributor Name].[Distributor Name].[Distributor Name].Members
*
[dimension of Geography Country].[Geography Country].[Geography Country].Members
*
Tail([Date].[Date - Calendar Month].[Calendar Day].Members,60)
*
{
[Measures].[Revenue],
[Measures].[NumClients]
}
ON ROWS
FROM [OURCUBE]
There is no need to define sets here. you can directly state the distributor and country members in the rows clause.
By repeating the attribute name, you restrict the attribute hierarchy - which you refer to by [dim].[attrib name] to the level below the All member, which happens to have the same name as the attribute again. An attribute hierarchy has two levels: level 0 contains the 'All' member and level 1 all the members of the attribute. (This is true only if you did not do special configurations like setting the attribute as non aggregateabable, but I assume the standard case, as you have All members in your hierarchies.
Apart from being more simple, this statement will run much faster, as Filter is a real performance killer in many cases.
I would use the Descendants function and the AFTER option as following; this way you get all the members of the hierarchy below the all member:
select
[Measures].[Amount] on 0,
Descendants([Customers].[Geography].[All], 1, AFTER ) on 1
from [Sales]
(edited: with a request working with MSAS Adv. Works : removed the distance param)
select
Measures].[Order Count] on 0,
Descendants( [Geography].[Geography].[All], , AFTER ) on 1
from [Adventure Works]