I'm just starting to get my head around MDX and I'm having trouble with a calculated member. I'm using the following MDX:
IIF(
ISEMPTY((Axis(1).Item(0).Item(0).Dimension.CurrentMember, [Measures].[Qty]))
,NULL
,([Product].[Product Code].CurrentMember.Parent, [Measures].[Qty])
)
What I'm trying to do is get a total quantity of the group of products displayed in a cube. I then use that total to divide by each product's quantity to get a "percent of total" measure. The above MDX does correctly return the total quantity of products displayed in any dimension. However, when a user in Excel changes the filter on which products are displayed, the MDX above still displays the total quantity for the whole group, ignoring which products the user has checked. I assume I'm lacking some basic understanding of MDX, how do I get the calculated measure to account for the product codes the user has selected in Excel?
With Visual Studio SQL Server Data Tools (if you are using that tool), you can browse to your cube, select the Calculations tab, and under Calculations Tools > Templates there is a template "Percentage of Total". The MDX this tool provides is flexible so that the percentage adjusts with the attributes of the hierarchy you've pulled into the pivot.
Case
// Test to avoid division by zero.
When IsEmpty
(
[Measures].[<<Target Measure>>]
)
Then Null
Else ( [<<Target Dimension>>].[<<Target Hierarchy>>].CurrentMember,
[Measures].[<<Target Measure>>] )
/
(
// The Root function returns the (All) value for the target dimension.
Root
(
[<<Target Dimension>>]
),
[Measures].[<<Target Measure>>]
)
End
I found this option to work when developing to achieve what you've mentioned.
Related
DAX 2013 standalone power pivot.
I have a sales table with Product and Brand columns, and Sales measure which explicitly sums up sales column.
Task in hand: I need to create 1 measure RANK which would ...
if Product is filtered expressly, then return count of Products that have higher or equal sales amount, divided by total count of products.
If it's a subtotal brand level, show the same but for brands.
My current approach is using RANK and then MAXX of rank which seems working but a no-go - slow nightmare. Excel runs out of memory.
Research: it's been a week. This is the most relevant post i found anywhere, this question here , but it's in MDX.
In my example picture, I'm showing Excel formulas with which I can get to the result. Ideally there shouldn't be any helpers, 1 formula for all.
I.E.
RANK:=IF( HASONEFILTER(PRODUCTS[PRODUCT], HELPER_PROD, HELPER_BRAND)
where HELPER_PROD part would be something like this - need to find a way to refer to "current" result in pivot table like Excel does using [#[...:
HELPER_PROD:=COUNTX(ALL(PRODUCTS), [SALES]>=[#[SALES]]) / COUNTX(ALL(PRODUCTS))
HELPER_BRAND:=COUNTX(
DISTINCT(ALL(PRODUCTS[BRAND])),
[SALES]>=[#[SALES]]) /
COUNT(DISTINCT(ALL(PRODUCTS[BRAND]))
You can use the "Earlier" function to compare with the current record.
ProductsWithHigherSales:=CALCULATE(countrows(sales),
FILTER(all(Sales),
countrows(filter(Sales,Sales[Sales]<=EARLIER(Sales[Sales])))
))
Using Earlier function in measures: can-earlier-be-used-in-dax-measures
Used workbook: Excel File
I have this problem to find a generic MDX expression that returns the percent of grand total regardless of the dimension that i drag in the SSAS cube browser.
Now i'm using this expression:
([Measures].[Montant], Axis(1)(0)(Axis(1)(0).Count - 1).dimension.currentmember)
/SUM(([Measures].[Montant], Axis(1)(0)))
it works fine, but when i filter on the inner item of the axis, the expression returns a wrong value
For example :
i have in my rows axis 3 items : Year > Brand > Category
The grand total is 125 for all rows:
SUM(([Measures].[Montant], Axis(1)(0)))
If i filter on the categories , the grand total changes, lets say it is equal to 65 now for the outer items of the axis. But when i drill down to see its value for the categories, i find it still equal to 125. and as a result the value of percent is wrong as well.
Can someone please help me figure out what's wrong with my MDX expression coz i've been stuck at it for too long and i don't seem to find a solution.
screenshot of cube browser
The calculated measure is "test SOB", MDX expression :
([Measures].[Montant], Axis(1)(0)(Axis(1)(0).Count - 1).dimension.currentmember)
/SUM(([Measures].[Montant], Axis(1)(0)))
the grand total is "denominateur", MDX expression:
SUM(([Measures].[Montant], Axis(1)(0)))
as you can see, the value after filtering with Onglet = "DIGITAL" is 182.50 but when I drill down the brand "Beauty" to see "denominateur" per category, i find the value 338.05 which is the value of "denominateur" before applying the filter.
I'm wondering if the use of EXISTING will enforce the filter context in your denominteur calculation?
SUM(
[Measures].[Montant],
EXISTING Axis(1).ITEM(0).ITEM(0).HIERARCHY.MEMBERS
)
BACKGROUND: I've been using MDX for a bit but I am by no means an expert at it - looking for some performance help. I'm working on a set of "Number of Stores Authorized / In-Stock / Selling / Etc" calculated measures (MDX) in a SQL Server Analysis Services 2012 Cube. I had these calculations performing well originally, but discovered that they weren't aggregating across my product hierarchy the way I needed them to. The two hierarchies predominantly used in this report are Business -> Item and Division -> Store.
For example, in the original MDX calcs the Stores In-Stock measure would perform correctly at the "Item" level but wouldn't roll up a proper sum to the "Business" level above it. At the business level, we want to see the total number of store/product combinations in-stock, not a distinct or MAX value as it appeared to do originally.
ORIGINAL QUERY RESULTS: Here's an example of it NOT working correctly (imagine this is an Excel Pivot Table):
[FILTER: CURRENT WEEK DAYS]
[BUSINESS] [AUTH. STORES] [STORES IN-STOCK] [% OF STORES IN STOCK]
[+] Business One 2,416 2,392 99.01%
[-] Business Two 2,377 2,108 93.39%
-Item 1 2,242 2,094 99.43%
-Item 2 2,234 1,878 84.06%
-Item 3 2,377 2,108 88.68%
-Item N ... ... ...
FIXED QUERY RESULTS: After much trial and error, I switched to using a filtered count of a CROSSJOIN() of the two hierarchies using the DESCENDANTS() function, which yielded the correct numbers (below):
[FILTER: CURRENT WEEK DAYS]
[BUSINESS] [AUTH. STORES] [STORES IN-STOCK] [% OF STORES IN STOCK]
[+] Business One 215,644 149,301 93.90%
[-] Business Two 86,898 55,532 83.02%
-Item 1 2,242 2,094 99.43%
-Item 2 2,234 1,878 99.31%
-Item 3 2,377 2,108 99.11%
-Item N ... ... ...
QUERY THAT NEEDS HELP: Here is the "new" query that yields the results above:
CREATE MEMBER CURRENTCUBE.[Measures].[Num Stores In-Stock]
AS COUNT(
FILTER(
CROSSJOIN(
DESCENDANTS(
[Product].[Item].CURRENTMEMBER,
[Product].[Item].[UPC]
),
DESCENDANTS(
[Division].[Store].CURRENTMEMBER,
[Division].[Store].[Store ID]
)
),
[Measures].[Inventory Qty] > 0
)
),
FORMAT_STRING = "#,#",
NON_EMPTY_BEHAVIOR = { [Inventory Qty] },
This query syntax is used in a bunch of other "Number of Stores Selling / Out of Stock / Etc."-type calculated measures in the cube, with only a variation to the [Inventory Qty] condition at the bottom or by chaining additional conditions.
In its current condition, this query can take 2-3 minutes to run which is way too long for the audience of this reporting. Can anyone think of a way to reduce the query load or help me rewrite this to be more efficient?
Thank you!
UPDATE 2/24/2014: We solved this issue by bypassing a lot of the MDX involved and adding flag values to our named query in the DSV.
For example, instead of doing a filter command in the MDX code for "number of stores selling" - we simply added this to the fact table named query...
CASE WHEN [Sales Qty] > 0
THEN 1
ELSE NULL
END AS [Flag_Selling]
...then we simply aggregated these measures as LastNonEmpty in the cube. They roll up much faster than the full-on MDX queries.
It should be much faster to model your conditions into the cube, avoiding the slow Filter function:
If there are just a handful of conditions, add an attribute for each of them with two values, one for condition fulfilled, say "cond: yes", and one for condition not fulfilled, say "cond: no". You can define this in a view on the physical fact table, or in the DSV, or you can model it physically. These attributes can be added to the fact table directly, defining a dimension on the same table, or more cleanly as a separate dimension table referenced from the fact table. Then define your measure as
CREATE MEMBER CURRENTCUBE.[Measures].[Num Stores In-Stock]
AS COUNT(
CROSSJOIN(
DESCENDANTS(
[Product].[Item].CURRENTMEMBER,
[Product].[Item].[UPC]
),
DESCENDANTS(
[Division].[Store].CURRENTMEMBER,
[Division].[Store].[Store ID]
),
{ [Flag dim].[cond].[cond: yes] }
)
)
Possibly, you even could define the measure as a standard count measure of the fact table.
In case there are many conditions, it might make sense to add just a single attribute with one value for each condition as a many-to-many relationship. This will be slightly slower, but still faster than the Filter call.
I believe you can avoid the cross join as well as filter completely. Try using this:
CREATE MEMBER CURRENTCUBE.[Measures].[Num Stores In-Stock]
AS
CASE WHEN [Product].[Item Name].CURRENTMEMBER IS [Product].[Item Name].[All]
THEN
SUM(EXISTS([Product].[Item Name].[Item Name].MEMBERS,[Business].[Business Name].CURRENTMEMBER),
COUNT(
EXISTS(
[Division].[Store].[Store].MEMBERS,
(
[Business].[Business Name].CURRENTMEMBER,
[Product].[Item Name].CURRENTMEMBER
),
"Measure Group Name"
)
))
ELSE
COUNT(
EXISTS(
[Division].[Store].[Store].MEMBERS,
(
[Business].[Business Name].CURRENTMEMBER,
[Product].[Item Name].CURRENTMEMBER
),
"Measure Group Name"
)
)
END
I tried it using a dimension in my cube and using Area-Subsidiary hierarchy.
The case statement handles the situation of viewing data at Business level. Basically, the SUM() across all members of Item Names used in CASE statement calculates values for individual Item Names and then sums up all the values. I believe this is what you needed.
I'm not very experienced in OLAP Cube + MDX, and I'm having a hard time trying to use twice the same measure in a cube.
Let's say that we have 3 Dimensions: D_DATE, D_USER, D_TYPE_OF_SALE_TARGET and 3 tables of Fact: F_SALE, F_MEETING, F_SALE_TARGET
F_SALE is linked to D_USER (who make the sale) and D_DATE (when)
F_SALE_TARGET is linked to D_USER, D_DATE, D_TYPE_OF_SALE_TARGET (meaning: user has to reach various goals/targets for a given month).
I can browse my cube:
Rows = Date * User
Cols = Number of sale, Total amount of sale + the value of 1 target (in the WHERE clause, I filter on [Dim TYPE SALE TARGET].[Code].&[code.numberOfSales])
How can I add other columns for other targets? As all the targets are in the same table, I don't see how to add a second measure from [Measures].[Value - F_SALE_TARGET] linked to a different code, ie. [Dim TYPE SALE TARGET].[Code].&[code.amountOfSale].
your question is not clear to me but it seems like one way to accomplish that is by creating Calculated Members. Basically, select you cube in BIDS, go to the Calculations tab and create Calculated Members. You would be able to insert your MDX query there. For each target type you can create a different calculation such as: ([Measures].[Value - F_SALE_TARGET], [Dim TYPE SALE TARGET].[Code].&[code.amountOfSale])
I'm creating Analysis Services cubes in Visual Studio BIDS, and have a question about summing in calculated members.
The data has to do with commercial real estate transactions. I want to sum square feet of building space involved in sales transactions for each region. I'm going to use that result in a weighted average calculation. However, I only want to sum the square feet of transactions which have non-null values for the corresponding building capitalization rate (cap rate) member.
Here is a drill-down to Athens in the cube browser:
Note that Athens has 15 values for square feet, but only 5 values for cap rate, reflecting my relational data source as shown here:
So, I only want to sum the five square feet values that have associated cap rate values. Doing the math with the relational query result above you can see that this should result in a sum just over 900K, not the 2 million+ sum shown in the BIDS screenshot.
My attempt at this calculation:
sum(
descendants(
[Property].[Property by Region].CurrentMember,
[Property].[Property by Region].[Metro Area]
),
iif([Measures].[Cap Rate] is null or [Measures].[Sq Ft] is null, 0,
[Measures].[Sq Ft])
)
ends up including the square feet values that have no corresponding cap rates, so I still end up with a value in the 2 millions.
Why is my iff() clause not working as one would expect?
I was finally able to create the weighted average calculation using a combination of Named Calculations in the Data Source View (DSV) and a calculated member (in the cube script). First, I went to the DSV and added a named calculation called xWeightedCapRt with a formula as follows:
CASE WHEN CapRate IS Null THEN Null Else CapRate * SqFt END
In the cube, I then added xWeightedCapRt as a New Measure. I set its aggregation function to Sum and left its Visible property set to True temporarily.
I created an additional Named Calculation called "xSqFt", defined as:
CASE WHEN CapRate IS Null THEN Null Else SqFt END
and again created a corresponding measure.
On the Calculation tab (of the cube designer) I created a new calculated member, [WAvg Cap Rate by Sq Ft], with the following formula:
[Measures].[x Weighted Cap Rt] / [Measures].[x Sq Ft]
After deploying and processing the cube, I was able to verify that the weighted average calculation matched my spreadsheet numbers. At that point, I set the Visible property of the two intermediate measures to False and redeployed.
What I've learned is that calculations at the "row-level" are best performed through the DSV. You can then use those to build up more complex calculations within the cube.
(NOTE: One thing that needs to be added to the steps above is logic to handle division by zeros.)
Couldnt you have done a nonempty around the descendants on the cap rate measure?