Excluding (All) from sets - ssas

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]

Related

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

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.

How to mimic SQL subtraction of results from two different queries in mdx

I wanted to do the trend analysis between the dates. For an instance current date- 30 days
30-60 days and so on.Below is the snippet of comparable sql query but same I wanted to do in MDX.
SQL
SELECT
ROUND
(
(
(
(
SELECT
SUM(del_pri_impr)
FROM
reporting.so_sli_calc_val a,
reporting.user_group_tenant b,
reporting.salesorder c
WHERE
created_on BETWEEN DATE(now()-30) AND DATE(now())
)
-
(
SELECT
SUM(del_pri_impr)
FROM
reporting.so_sli_calc_val a,
reporting.user_group_tenant b,
reporting.salesorder c
WHERE
created_on BETWEEN DATE(now()-60) AND DATE(now()-30)
)
)
/
(
SELECT
SUM(del_pri_impr)
FROM
reporting.so_sli_calc_val a,
reporting.user_group_tenant b,
reporting.salesorder c
WHERE
created_on BETWEEN DATE(now()-60) AND DATE(now()-30)
) *100
)
,
0
) AS trend
MDX:
WITH
SET [~FILTER] AS
{[Created_Date.Created_Hir].[Created_On].[2014-04-01]:[Created_Date.Created_Hir].[Created_On].[2014-04-30]}
SET [~ROWS] AS
{[Sales Order Attributes SO.Sales_order].[Sales Order ID].Members}
SELECT
NON EMPTY {[Measures].[CONT_AMT_GROSS], [Measures].[CONT_AMT_NET]} ON COLUMNS,
NON EMPTY [~ROWS] ON ROWS
FROM [SALES_ORDER]
WHERE [~FILTER]
As of now I have hard coded the dates, that will come from parameters.
I am facing difficulty in creating the second set and how to do subtraction between two sets in MDX.
You already have the logic on how to obtain sets of date corresponding to "last 30 days from now" and "last 60 to last 30 days from now". So, I am going to skip that part.
NOTE - You would have to use the parameter values while building these sets.
What you want to do here is first find the values corresponding to these sets of dates and then perform operations on them.
You can proceed like this -
WITH
SET [~FILTER] AS
{[Created_Date.Created_Hir].[Created_On].[2014-04-01]:[Created_Date.Created_Hir].[Created_On].[2014-04-30]}
SET [~ROWS] AS
{[Sales Order Attributes SO.Sales_order].[Sales Order ID].Members}
SET [Last30Days] AS
...
SET [Last60ToLast30Days] AS
...
MEMBER [~Last30Days - Now] AS
Aggregate
(
[Last30Days],
[Measures].[SomeMeasure]
)
MEMBER [~Last60Days - Last30Days] AS
Aggregate
(
[Last60ToLast30Days],
[Measures].[SomeMeasure]
)
MEMBER [~Measure] AS
([~Last30Days - Now]-[~Last60Days - Last30Days] )/([~Last60Days - Last30Days] * 100), format_string = '#,##0'
SELECT
NON EMPTY {
[Measures].[CONT_AMT_GROSS],
[Measures].[CONT_AMT_NET],
[~Measure]
} ON COLUMNS,
NON EMPTY [~ROWS] ON ROWS
FROM [SALES_ORDER]
Format_String takes care of rounding.
Not sure if I totally agree with Sourav's answer as I think some form of aggregation will be needed; creating tuples with sets in them may raise an exception.
Here is a simple model, against AdvWrks, that is tested and will do a subtraction for you:
WITH
SET [Set1] AS
[Date].[Calendar].[Date].&[20060301]
:
[Date].[Calendar].[Date].&[20070308]
SET [Set2] AS
[Date].[Calendar].[Date].&[20070308]
:
[Date].[Calendar].[Date].&[20080315]
MEMBER [Date].[Calendar].[All].[Set1Agg] AS
aggregate([Set1])
MEMBER [Date].[Calendar].[All].[Set2Agg] AS
aggregate([Set2])
MEMBER [Date].[Calendar].[All].[x] AS
(
[Date].[Calendar].[All].[Set1Agg]
,[Measures].[Internet Sales Amount]
)
MEMBER [Date].[Calendar].[All].[y] AS
(
[Date].[Calendar].[All].[Set2Agg]
,[Measures].[Internet Sales Amount]
)
MEMBER [Date].[Calendar].[All].[x-y] AS
[Date].[Calendar].[All].[x] - [Date].[Calendar].[All].[y]
SELECT
{
[Date].[Calendar].[All].[x]
,[Date].[Calendar].[All].[y]
,[Date].[Calendar].[All].[x-y]
} ON 0
,[Product].[Category].[Category] ON 1
FROM [Adventure Works];
Reflecting against your code maybe something like the following:
WITH
SET [Set1] AS
[Created_Date.Created_Hir].[Created_On].[2014-04-01]
:
[Created_Date.Created_Hir].[Created_On].[2014-04-30]
SET [Set2] AS
[Created_Date.Created_Hir].[Created_On].[2014-03-01]
:
[Created_Date.Created_Hir].[Created_On].[2014-03-31]
MEMBER [Created_Date.Created_Hir].[All].[Set1Agg] AS
Aggregate([Set1])
MEMBER [Created_Date.Created_Hir].[All].[Set2Agg] AS
Aggregate([Set2])
MEMBER [Measures].[~Last30Days - Now] AS
(
[Created_Date.Created_Hir].[All].[Set1Agg]
,[Measures].[SomeMeasure]
)
MEMBER [Measures].[~Last60Days - Last30Days] AS
(
[Created_Date.Created_Hir].[All].[Set2Agg]
,[Measures].[SomeMeasure]
)
MEMBER [Measures].[~Measure] AS
([Measures].[~Last30Days - Now] - [Measures].[~Last60Days - Last30Days])
/
[Measures].[~Last60Days - Last30Days]
* 100
,format_string = '#,##0'
SET [~ROWS] AS
{
[Sales Order Attributes SO.Sales_order].[Sales Order ID].MEMBERS
}
SELECT
NON EMPTY
{
[Measures].[CONT_AMT_GROSS]
,[Measures].[CONT_AMT_NET]
,[Measures].[~Measure]
} ON COLUMNS
,NON EMPTY
[~ROWS] ON ROWS
FROM [SALES_ORDER]
WHERE
[~FILTER];

MDX Multi Level Select

I have
Geography Dimension
-> Admin Hierarchy
-> Province level
-> District level
-> Facility level.
Now I want to find the geography data when province = 'BeiJing' and facility = 'test'. How should I do ?
I write MDX code like this:
SELECT
{} ON 0,
{[Geography.Admin].[BeiJing].[All Geography.Admin].[test]} ON 1
FROM [EIDCube]
but It doesn't work. How can I get all province/district/facility data as table when I set special province and facility ?
In AdvWrks the following throws an error
Query (4, 5) The Customer Geography hierarchy is used more than once
in the Crossjoin function.
This is thrown when I try to run the following:
SELECT
[Measures].[Internet Sales Amount] ON 0
,
[Customer].[Customer Geography].[Country]
*
[Customer].[Customer Geography].[State-Province] ON 1
FROM [Adventure Works];
The way around this is to use the attribute hierarchies which the levels of the [Customer Geography] user hierarchy is made up of, like this:
SELECT
[Measures].[Internet Sales Amount] ON 0
,
[Country].[Country]
*
[State-Province].[State-Province] ON 1
FROM [Adventure Works];
Now if I want Australia and Queensland I can cross-join a couple of single member sets:
SELECT
[Measures].[Internet Sales Amount] ON 0
,
{[Country].[Country].[Australia]}
*
{[State-Province].[State-Province].[Queensland]} ON 1
FROM [Adventure Works];
Results in this:
Edit
Applying the above to your structure you should be able to do something like this:
SELECT
{[Measures].[someMeasureInCube]} ON 0
,
{[Province].[Province].[BeiJing]}
* {[Facility].[Facility].[test]} ON 1
FROM [EIDCube];
Assuming your hierarchichal structure to be
Geography-->Geography.Admin-->Province-->District-->Facility
(dimension) (hierarchy) (level 1) (level 2) (level 3)
Here is the MDX to do it the long way(short way being directly feeding the values in query, which surprisingly didn't work)
select [Geography].[Geography.Admin].CHILDREN
having
[Geography].[Geography.Admin].currentmember.member_caption = "test" and
ancestors([Geography].[Geography.Admin].currentmember, 3).item(0).member_caption = "BeiJing"
on 1,
{} on 0
from EIDCube]
I think you can use this :
SELECT
{} ON 0,
([Geography].[Admin].[BeiJing],[Geography].[Admin].[Province].[District].[test]) ON 1
FROM [EIDCube]
Edit 1 : My previous answer was wrong.
So now I think the better solution is to create a new hierarchy [admin2] for the geography dimension composed of the level [Facility] only and to use this query.
SELECT
{} ON 0,
([Geography].[Admin].[BeiJing],[Geography].[Admin2].[Facility].[test]) ON 1
FROM [EIDCube]

Search AllMembers of User Hierarchy - for corresponding Set return a related members from upper and lower levels

We have a user hierarchy with 3 levels (Level1 being the upper level - Level3 the lower)
Using the FILTER function I state a string, this string could be within the member_name of any member from any (or several) of the levels. This returns a set.
I would then like all members from the hierarchy related to each item in the set; both upper and lower levels.
I've also tried (unsuccessfully) to include a column that is always Level2 in the results as a reference and also an ordering column.
WITH
MEMBER [Measures].[LevelName] AS
[DimensionX].[HierX].Level.Name
MEMBER [Measures].[LevelNumber] AS
[DimensionX].[HierX].Level.Ordinal
MEMBER [Measures].[MemName] AS //<<<<<not working as I want level 2 in the result set
[DimensionX].[HierX].currentmember.membervalue
SET [Set_Target] AS
{
FILTER(
[DimensionX].[HierX].AllMembers,
(
InStr(
1,
[DimensionX].[HierX].currentmember.name,
"jesus") <> 0
)
)
}
SELECT
// ORDER(
// Exists(
// [AttributeHierX].members,
// [Set_Target]
// ),
// [AttributeHierX].[AttributeHierX].MEMBERVALUE
// )
//*
ORDER(
DESCENDANTS(
[Set_Target],
[DimensionX].[HierX].[Level1],
SELF_BEFORE_AFTER
),
[Measures].[LevelNumber],
BASC
) as X
ON 1,
{
[Measures].[MemName],
[Measures].[LevelName],
[Measures].[LevelNumber]
} ON 0
FROM [CubeX]
I suppose a similar query on AdventureWorks delivers what you want:
WITH
MEMBER [Measures].[LevelName] AS
[Employee].[Employee Department].CurrentMember.Level.Name
MEMBER [Measures].[LevelNumber] AS
[Employee].[Employee Department].CurrentMember.Level.Ordinal
MEMBER [Measures].[MemName] AS
[Employee].[Employee Department].CurrentMember.Name
SET [Set_TargetEmp] AS
{
FILTER(
[Employee Department].AllMembers,
(
InStr(
1,
[Employee].[Employee Department].currentmember.name,
"WC4") <> 0
)
)
}
SELECT
{
[Measures].[MemName],
[Measures].[LevelName],
[Measures].[LevelNumber]
} ON 0,
Hierarchize(
Generate([Set_TargetEmp] as e,
Ascendants(e.Current)
+
Descendants(e.Current, e.Current.Level, SELF_AND_AFTER)
)
)
ON 1
FROM [Adventure Works]
I used Hierarchize to sort the result by hierarchy, as that seemed the most easy way for me to check the result. You may want to change that. As - in contrast to the Descendants method - Ascendants does not allow a set as the first argument, I used Generate to iterate along the set. Its default behavior (without a third argument of All), it eliminates duplicates, which I assumed is the behavior you need.

Adding further information in the context of the cell set

I'd like to be able to search the [Employee Department] hierarchy - within any of it's levels using a string: the following does this ok and finds all members related to the string "Control"
Now I have tried to add a cross join so that I can always see the department name for each of the rows and then ORDER by the department. If the commented section is uncommented I unfortunately get a full cartesian product - I only want the departments in the context of the members found by the filter - is this possible?
WITH
MEMBER [Measures].[LevelName] AS
[Employee].[Employee Department].Level.Name
MEMBER [Measures].[LevelNumber] AS
[Employee].[Employee Department].Level.Ordinal
SET [Set_TargetEmp] AS
{
FILTER(
[Employee Department].AllMembers,
(
InStr(
1,
[Employee].[Employee Department].currentmember.name,
"Control") <> 0
)
)
}
SELECT
// ORDER(
// [Department].members,
// [Department].[Department].MEMBERVALUE
// )
// *
ORDER(
DESCENDANTS(
[Set_TargetEmp],
[Employee].[Employee Department].[Department],
SELF_BEFORE_AFTER
),
[Measures].[LevelNumber],
BASC
) as X
ON 1,
{
[Measures].[LevelName],
[Measures].[LevelNumber]
} ON 0
FROM [Adventure Works]
Assuming you use Department the Department dimension, and the Employee Department is in a different dimension named Employee, you get a cross product. Analysis Services only applies "autoexists" within the same dimension. Across dimensions, you must apply this logic explicitly like this:
ORDER(
Exists([Employee].[Department Name].[Department Name].members,
[Set_TargetEmp]
),
[Department].[Department].MEMBERVALUE
)
for the commented block in your code should deliver what you want.
In case you have more than one measure group that relate to both the department and the employee dimensions, you should state the name of the measure group to use as the third argument of Exists. This is a string argument, hence this name should be included in quotes.