Old SQL developer, new to MDX: Need help with a requirement to include small totals in a running total but not show the contributing rows of those small totals.
Consider this data
Amount Running Total
Denver 6,321 6,321
Portland 8,426 14,747
Boise 19,222 33,969
Helena 23,257 57,226
Bozeman 31,225 88,451
Seattle 36,894 125,345
My requirement is to not show any amounts under 15,000 but instead show the running total that includes them. I must not show amounts or running totals less than 15,000. This is to insure that small numbers can't be identified to specific cities.
like this:
Amount Running Total
Other 19,222 33,969
Helena 23,257 57,226
Bozeman 31,225 88,451
Seattle 36,894 125,345
Or, ideally, what is needed:
Amount
Other 33,969
Helena 23,257
Bozeman 31,225
Seattle 36,894
Thanks for any assistance
MartinA
After a few whiteboard sessions, my co-conspirator came up with a simple solution: Add a new measure, calculating the difference between the running total and the city amount. When this difference is less than the threshold, then this will be the rolled up row that is to contain the City name of “Other” and also use the running total rather than city total. Kudos to Tyson
Here's some example code:
WITH
SET [OrderedSet] AS
Nonempty
(
ORDER
([Age].[Age Group B].[Age Group B].Members,
[Measures].[Emergency Room Visits per 1,000 Member Months],
BASC
)
)
MEMBER [Measures].[RowNumber] AS
Rank([Age].[Age Group B].CURRENTMEMBER,
[OrderedSet]
)
MEMBER [Measures].[Running Total] as
Sum(
Head
([OrderedSet],
([Measures].[RowNumber],[Age].[Age Group B].CurrentMember)
),
[Measures].[Emergency Room Visits per 1,000 Member Months]
)
MEMBER [Measures].[Ttl_RunTtl_Diff] AS
[Measures].[Running Total] - [Measures].[Emergency Room Visits per 1,000 Member Months]
MEMBER MEASURES.NewAge AS
IIF([Measures].[Ttl_RunTtl_Diff] = 0 OR [Measures].[Ttl_RunTtl_Diff]>15000
, [Age].[Age Group B].CURRENTMEMBER.Name, "Other")
MEMBER MEASURES.NewTotal AS
IIF([Measures].[Ttl_RunTtl_Diff] = 0 OR [Measures].[Ttl_RunTtl_Diff]>15000
, [Measures].[Emergency Room Visits per 1,000 Member Months], [Measures].[Running Total])
SELECT NON EMPTY
{
[Measures].[Emergency Room Visits per 1,000 Member Months],
[Measures].[Member Months],
[Measures].[Emergency Room Visits],
[Measures].[Running Total],
[Measures].[Ttl_RunTtl_Diff],
[Measures].[NewAge],
[Measures].[NewTotal]
}
ON COLUMNS,
NON EMPTY
FILTER({[OrderedSet]}
, [Measures].[Running Total] > 15000 )
ON ROWS FROM [Model]
Related
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
The below MDX works for my purpose but is extremely slow. Is there a scope statement Essentially I want to count the remaining cross joined contact/purchases combinations where the summed amount is $>5000.
The cross product is a total of 290M rows but I am not sure how to structure this differently to improve performance. Thank you for any help.
CREATE HIDDEN STATIC SET [Over 5K Plus Test 2]
AS NONEMPTY (([Contact].[Contact ID].[Contact ID],[Fund Sold].[Fund Sold ID].[Fund Sold ID]),
[Measures].[FA And Team Gross Sales with FAs Including All Vehicles]);
CREATE MEMBER CURRENTCUBE.[Measures].[FA and Team Product Count]
AS COUNT(EXISTING((Filter([Over 5K Plus Test 2], [Measures].[FA And Team Gross Sales with FAs Including All Vehicles] >= 5000)))),
Try this which avoids the Filter:
CREATE MEMBER CURRENTCUBE.[Measures].[FA and Team Product Count]
AS SUM(
Existing [Contact].[Contact ID].[Contact ID].Members
* Existing [Fund Sold].[Fund Sold ID].[Fund Sold ID].Members,
IIF([Measures].[FA And Team Gross Sales with FAs Including All Vehicles] >= 5000, 1, Null)
);
If that is still slow then post the calculation behind FA And Team Gross Sales with FAs Including All Vehicles
The more efficient way to accomplish this requires a bit more effort but will perform better because it avoids the Existing function. First you have to create a column in the DSV which is a calculated column in the fact table using this expression:
CAST(null as int)
Then create a new measure called “FA and Team Product Count” on this column. Expand the column binding and choose NullHandling=Preserve. This has to be a physical measure not a calculated measure because only scoped assignments to physical measures aggregate up.
Then add the following statement to the MDX script (instead of the calculated measure mentioned at the top):
([Measures].[FA and Team Product Count],
[Contact].[Contact ID].[Contact ID].Members,
[Fund Sold].[Fund Sold ID].[Fund Sold ID].Members) =
IIF([Measures].[FA And Team Gross Sales with FAs Including All Vehicles] >= 5000, 1, Null);
We have a cube that it has one measure (Commission Amount) and two dimensions customer and Date.
I want calculate ratio of (count of customers who create 80 percent of Commission Amount) to (count of total customer)
It is important to say that customers are sorted base on their Commission Amount
How can solve this problem? And what must use to solve this query?
You want to use the TopPercent() function here:
TopPercent(
existing [Customer].[Customer].[Customer].Members,
80,
[Measures].[Commission Amount]
)
/
existing [Customer].[Customer].[Customer].Members.Count
Basically the same approach as Danylo - I just added an extra COUNT and deleted some, possibly redundant, EXISTING keywords:
DIVIDE(
COUNT(
TOPPERCENT(
{[Customer].[Customer].[Customer].MEMBERS},
80,
[Measures].[Commission Amount]
)
)
,[Customer].[Customer].[Customer].MEMBERS.COUNT
)
I'm looking for help with how to write a specific DAX measure. Here is a simplified version of my data and model:
Tables:
Model:
Measures:
Total Amt:=SUM(Amounts[Amt])
Total Pos Amt:=SUMX(Amounts, IF([Amt]<0,0,[Amt]))
Total Amt All:=CALCULATE([Total Pos Amt],ALL(Ptr))
Total Amt All 2:=SUMX(Bridge,CALCULATE([Total Pos Amt],ALL(Ptr)))
Total Amt All 3:=SUMX(VALUES(Bridge[Pri]),CALCULATE([Total Pos Amt],ALL(Ptr)))
As you can see in the first PivotTable (where [Pri] & [Ptr] are row fields), the highlighted cells show values with an issue. The [Total Pos Amt] measure sums up the [Amt] column in the Amounts table by iterating through it and evaluating an expression where negative amounts are treated as zero and positive amounts are kept. At a [Pri] level granularity, I want that same logic to apply (i.e. evaluate the expression at a [Ptr] level). The problem with the [Total Amt All] measure is that on the PivotTable I get a row for [Ptr] Z under [Pri] A which I don't want. Measures [Total Amt All 2] and [Total Amt All 3] solve that issue but the subtotals at a [Pri] level are wrong in [Total Amt All 2] and the grand total is wrong in [Total Amt All 3].
Any help would be greatly appreciated! How can I write a measure that won't show a [Ptr] that is not associated with a [Pri] per the Bridge table, but that also correctly sums up the [Total Pos Amt] measure at a [Pri] level?
So one of your problems might be which fields you're using in your PivotTable. I got it work by using your bridge table as the fields:
TotalAmtBridged:=CALCULATE ( SUMX(Amounts, IF([Amt]<0,0,[Amt]) ) , Bridge )
FinalTotalAmt:= Calculate([TotalAmtBridged], ALL(Bridge[Ptr])
And then the PivotTable uses Bridge[Pri] and Bridge[Ptr]. So TotalAmtBridged just forces your total amount to use the Bridge context, and then FinalTotal says ignore Ptr (i.e. for each row we're displaying figure out the total amount for Bridge[Pri] only).
And then the grand total's already doing that, so Bob's your uncle.
WITH MEMBER [Measures].[NetPromoterScore] AS (IIF(([Measures].[Net Promoter Score] = 1/0 OR
[Measures].[Net Promoter Score] = -1/0 OR ISEMPTY([Measures].[Net Promoter Score])
OR [Measures].[Avg Revenue Per Unit] = 1/0 OR [Measures].[Avg Revenue Per Unit] = -1/0 OR
ISEMPTY([Measures].[Avg Revenue Per Unit]) ), NULL, [Measures].[Net Promoter Score]))
MEMBER [Measures].[AvgRevenuePerUnit] AS (IIF(([Measures].[Net Promoter Score] = 1/0
OR [Measures].[Net Promoter Score] = -1/0 OR ISEMPTY([Measures].[Net Promoter Score])
OR [Measures].[Avg Revenue Per Unit] = 1/0 OR [Measures].[Avg Revenue Per Unit] = -1/0
OR ISEMPTY([Measures].[Avg Revenue Per Unit]) ), NULL, [Measures].[Avg Revenue Per Unit]))
SELECT NON EMPTY
{ {[Measures].[NetPromoterScore],[Measures].[AvgRevenuePerUnit]} }
ON COLUMNS ,
NON EMPTY
{{Hierarchize(DrilldownLevel({[Roles].[Enterprise Role].[ALL]}))}}
DIMENSION PROPERTIES MEMBER_CAPTION, MEMBER_UNIQUE_NAME
ON ROWS
from Enterprise
WHERE (FILTER( [Employees].[EID].[EID],[Measures].[Avg Revenue Per Unit]> 700),
{[Areas].[Area].&[3]},
{[Markets].[Market].&[1]},{[Regions].[Region].&[2]},{[Locations].[Location].&[6],[Locations].[Location].&[6]},
{[Dates].[Date].&[20130219]:[Dates].[Date].&[20130318]})
As you see I have aliased [Net Promoter Score] column name with [NetPromoterScore] and [Avg Revenue Per Unit] column name with [AvgRevenuePerUnit]. But in my C# code, the names used are [Net Promoter Score] and [Avg Revenue Per Unit] and I can't change these previous name with new aliased name because I have to make changes in dozens of files and then I have to test whole application. Can I alias twice in the query above to get same previous column names or can I without aliasing get the same result from the query above?
Actually, in the sense of MDX, you have not aliased the members, but created new members.
I see two possibilities how you can solve this:
Move these calculations to the cube calculation script, i. e. rename
the original measures in the cube, and define calculated measures
with the name that you need and the same calculation that you have
in your query. You would then make the original renamed measures
invisible. This would be the best solution in my eyes, as it would
let every cube user benefit from the improvement, without breaking
external code or queries.
Depending on how exactly you reference the measures in your C# code
(you did not post samples), you could just use the caption property
of the measures, i. e. re-write the member definitions as follows:
WITH MEMBER [Measures].[NetPromoterScore] AS IIF(...), CAPTION = 'Net Promoter Score'
MEMBER [Measures].[AvgRevenuePerUnit] AS IIF(...), CAPTION = 'Avg Revenue Per Unit'