MDX - Top N elements of every subgroup - ssas

Let's assume I have a cube with a sales fact table and 3 dimensions: Time (with hierarchy year-month-day), Geography (with hierarchy continent-country-region) and Product with some characteristics about the product sold, let's take the brand for instance.
What I am trying to do is to display the top N brands with respect to the measure chosen, in this case average sales (which is already in the cube as total sales/number of products sold), for every region and month.
I also need to display the country and the year, for clearness.
I have searched for the solution to this everywhere, I came close to it but not completely. I hope someone can help me figure it out.
So I used generate and topcount with the following query, but the problem is that the topcount calculates the N best selling brands over the whole dataset, not for every region and month subgroup. And then applies this top N to every subgroup.
WITH SET [Top N Brands] AS
Generate([Geography].[Region].Children,
TopCount(Geography].[Region].CurrentMember * [Gpu Product].[Brand].[Brand].MEMBERS,
5, ([Measures].[averageSales])))
SELECT {[Measures].[averageSales]} ON COLUMNS,
NON EMPTY ( [Time].[Year].[Year],
[Time].[Month].[Month],
[Geography].[Country].[Country],
[Top N Brands]) ON ROWS
FROM [Cube]
So I am getting this, with the global top 5 brands distributed over the regions, if sold there:
But I should get this with different top 5s for every region:
What am I missing?

You need to use rank. Take a look at the example below. I am using the sample Adventure Works Db, here I am listing For each country, for each product category in that country, the top three subcategories according to internet sales.
WITH
MEMBER [Measures].[Internet Sales Amount Rank] AS
RANK( ([Customer].[Country].currentmember,[Product].[Category].currentmember,[Product].[Subcategory].CurrentMember),
ORDER( ([Customer].[Country].currentmember,[Product].[Category].currentmember,[Product].[Subcategory].[Subcategory].Members) , [Measures].[Internet Sales Amount], BDESC)
)
select
non empty
([Measures].[Internet Sales Amount])
on columns
,
non empty
([Customer].[Country].[Country],
[Product].[Category].[Category],
filter([Product].[Subcategory].[Subcategory],[Measures].[Internet Sales Amount Rank]<4))
on rows
from [Adventure Works]
Result

Related

Mondrian MDX - Filter Not Applying to Multiple Members Query

We currently have a query that shows the value of total quantity, or sales across store locations and departments, I intersect the quantity sold aggregated measure against locations, and the results are fine.
When we try to filter by total qty > 500, I am only seeing the sum of quantity evaluated by the location's value, not grouped by location and department.
HAVING is not seemingly supported, and if I include a where filter at the bottom, there is an issue with the same family/members being used multiple times and it's not allowed.
SELECT
{HEAD([dim_productfamily.hier_productfamily].[lvl_department].Members, 5)}
ON ROWS,
FILTER
(
{HEAD([dim_locations.hier_locations].[lvl_location].Members, 5) * [Measures].[total_qty]},
[Measures].[total_qty] > 500
)
ON COLUMNS
FROM
[sales_daily]
WHERE
{[dim_date.hier_date].[lvl_date].[20170521] : [dim_date.hier_date].[lvl_date].[20170730] }
The above query returns fine, but I get values that I've tested only really compare against the location sum(total_qty).
EDIT for Different Grouping
I tried using the below query, which seems to work. I think the way we render a table is improper in this case, as the output does seem to work fine.
SELECT
FILTER
(
{HEAD([dim_productfamily.hier_productfamily].[lvl_department].Members, 5) * HEAD([dim_locations.hier_locations].[lvl_location].Members, 5)},
[Measures].[total_qty] > 26
)
ON ROWS,
[Measures].[total_qty]
ON COLUMNS
FROM
[sales_daily]
WHERE
{[dim_date.hier_date].[lvl_date].[20170521] : [dim_date.hier_date].[lvl_date].[20170730] }
Is this what you were thinking?
The issue is that in your filter you really have not grouped by location and department. You have only grouped by location. An easy fix is the you bring both location and product on the same axis and that the measure on the oposite axis. Then filter it. It would work.
Looking at the problem again, you can query it in the below manner. This will let you use the orignal grouping.
WITH
MEMBER [Measures].[Data Type3] AS ([Geography].[Country].CurrentMember,[Product].[Product].CurrentMember,[Measures].[Reseller Sales Amount])
SELECT
FILTER
(
{[Product].[Product].Members}
,[Measures].[Reseller Sales Amount] > 2000
)
ON rows,
FILTER
(
([Geography].[Country].members, {[Measures].[Reseller Sales Amount]
,[Measures].[Data Type3]}),
[Measures].[Reseller Sales Amount] > 2000
)
ON columns
FROM
[Adventure Works]

TRIMMEAN in MDX

In Excel I can use this formula to calculate the average in a certain period and ignore the top 20% most extreme values: TRIMMEAN(AY13:BH13,20%)
I cannot find any result when searching on MDX TRIMMEAN. I think I have to do something with RANK and calculate the average by ignore X from the top and the bottom of the ranked result. Does somebody have an example of something like that?
You need to use BottomPercent. From Microsoft's documentation, if you want to find the measure value in the bottom 80% (exclude the top 20%), then you can have something like this:
SELECT [Measures].[Reseller Sales Amount] ON 0,
BottomPercent
({[Geography].[Geography].[City].Members}
, 80
, [Measures].[Reseller Sales Amount]
) ON 1
FROM [Adventure Works]
WHERE([Product].[Product Categories].[Bikes])
Further details can be found here BottomPercent

select the top 10 products of a given category

I am trying to select the top 10 products by internet sales from the Adenture Works cube (the cube I managed to compile with the Analysis Services Tutorial), I managed to do the cube perfectly, now I need to add a filter to add a specific product line and a given month and I don't know how to build the query...
I have this working:
with
set productNames as head(order({[Product].[Model Name].children}, [Measures].[Internet Sales-Unit Price], desc), 10)
select [Measures].[Internet Sales-Unit Price] on 0,
productNames on 1
from [Analysis Services Tutorial]
I added the filter and the result is the same...
with
set productNames as head(order(
filter({[Product].[Model Name].children},[Product].[Product Model Lines].[Product Line].&[R] )
, [Measures].[Internet Sales-Unit Price], desc), 10)
select [Measures].[Internet Sales-Unit Price] on 0,
productNames on 1
from [Analysis Services Tutorial];
Note: I can't use the hierarchy, because on my production database (which is not adventure works) I don't have hierarchies setup for the given problem.
Take a look at this query:
SELECT
[Measures].[Internet Sales Amount] ON COLUMNS
, TOPCOUNT([Product].[Product Line].&[R]*[Product].[Model Name].[Model Name],10,[Measures].[Internet Sales Amount]) ON ROWS
FROM
[Adventure Works]
The FILTER function is designed to filter a set of members that conform to a particular condition e.g. having Sales greater than £1000. It is not suitable for filtering a set by other dimensions or attributes. For this you can use the CROSSJOIN as I have done above (the *) and also the NONEMPTY function.
I hope that helps,
Ash

SSAS MDX query, Filter rows by sales people

I am learning how to query Cubes using MDX (SQL Server 2012) queries. I have been presented with a challenge. We have a hierarchy of sales people, a stored procedure returns a table with all sales people working under a manager. I have a classic sales cube where FactSales PK is invoice number and invoice line and a dimension for all our Sales people.
How can I filter the invoices where the sales person is in a table ?
Something like this but translated to MDX:
select * from sales where SalesPerson in (select SalesPerson from #salespeople)
The only way I see this could work is by writing the query dynamically and adding each salesperson in a filter, but that is not optimal in my opinion, we can have 200 or 400 people that we want to return sales from.
thanks!
If the dimension containing the sales people contains the hierarchy (who works for whom), you can resolve the challenge without using the stored procedure. Let's say your manager is named "John Doe" and your sales person hierarchy is named [Sales Person].[Sales Person]. Then just use
[Sales Person].[Sales Person].[John Doe].Children
in your query if you want to see sales for the people working directly for John, and you are done. In case you want to see John himself and everybody working for him directly or indirectly, you would use the Descendants function as follows:
Descendants([Sales Person].[Sales Person].[John Doe], 0, SELF_AND_AFTER)
This function has many variants, documented here.
In the Microsoft sample Adventure Works cube, where a similar hierarchy is called [Employee].[Employees], you could run the following query:
SELECT {
[Measures].[Reseller Sales Amount]
}
*
[Date].[Calendar].[Calendar Year].Members
ON COLUMNS,
Descendants([Employee].[Employees].[Jean E. Trenary], 0, SELF_AND_AFTER)
ON ROWS
FROM [Adventure Works]
to see the sales of employees working directly or indirectly for "Jean E. Trenary".

In MDX how can I filter a dimension/axis based on a TopCount

I have cube which contains geographic sales data. I want to pull sales of product items from all stores, based on the top 5 sellers from an arbitrary known store (BTW this is a simplified version of the real issue).
In sql (shame on me) this would be
select StoreId, ProductId, Sales from cube where ProductId in
(select top 5 ProductId from cube where Store = #Store order by Sales desc)
You can use the Topcount function for this. Provided you have dimensions named Product and Store and a measure named [Sales]:
Select [Measures].[Sales] On Columns,
CrossJoin([Store].Members,Generate(Topcount(Crossjoin({[Store].#[<StoreId>]},[Product].Members),5,[Measures].[Sales]),[Product].CurrentMember)) On Rows
From [Yourcube]
Replace <StoreId> with the specific store you're interested in. Generate will loop through the top 5 found for the specific store and return only the Product members. This result is then crossjoined with all the stores.
This will extract the top 5 products for your-store in ROWS axis and then select the [Sales] of these products according to the defaultMember of the [Store] dimension. In case [Store] defaultMember is the root of an aggregatable hierarchy this should give the sales of each product over all the stores.
SELECT
[Measures].[Sales] ON 0,
TopCount( [Store].[your-store] * [Product].Members ), 5, [Measures].[Sales] ) ON 1
FROM [your-cube]