MDX/SSAS sum of certain values over totals - calculate success/failure rate - ssas

I have a simplified example cube used for learning purposes, and to try to figure out a more complex problem.
The cube represents a small web server log,
number of hits as a measure
hostname as a dimension
http status code as a dimension
I can get a breakdown on number of hits per host and http status code with the MDX
SELECT NON EMPTY { [Measures].[CNT HITS] } ON COLUMNS,
NON EMPTY { ([DIM NOS STATUSCODE].[Statuscode].[Statuscode].ALLMEMBERS *
[DIM NOS HOST].[HOST].[HOST].ALLMEMBERS ) } ON ROWS
FROM [DW]
Now what I would like is to make groups over various HTTP status codes to e.g. show the percentage of successful hits (all 2xx status codes), the percentage unsuccessful hits (all non 2xx status codes).
I can do this with SQL, but I'm at a loss on how to do it with MDX. e.g. with SQL I'd do:
select HOST,
sum(CNT_HITS) as HITS ,
SUM(CASE WHEN s.statuscode div 100 = 2 THEN CNT_HITS ELSE 0 END)/sum(CNT_HITS) * 100 as success_percent,
SUM(CASE WHEN s.statuscode div 100 = 2 THEN 0 ELSE CNT_HITS END)/sum(CNT_HITS) * 100 as failed_percent,
sum(CASE WHEN s.statuscode = 401 THEN CNT_HITS ELSE 0 END)/sum(CNT_HITS) * 100 as auth_fail_percent
from FACT_NOS_HTTPLOG fact
group by HOST;
And for the data shown in the above screenshot, I'd get
+-----------------+------+-----------------+----------------+-------------------+
| HOST | HITS | success_percent | failed_percent | auth_fail_percent |
+-----------------+------+-----------------+----------------+-------------------+
| www.example.com | 1610 | 93.1677 | 6.8323 | 6.2112 |
| www.test.com | 50 | 0.0000 | 100.0000 | 0.0000 |
+-----------------+------+-----------------+----------------+-------------------+
But how can I accomplish this with MDX ?

I think the easiest way to accomplish this is to add a column to your fact table (or view/query) that would contain keys for either success_percent, failed_percent or auth_fail_percent. Then create a new dimension with these 3 members. Join to the fact and you have your solution without the need for any MDX at all.

Add an extra attribute [Status] to your [DIM NOS STATUSCODE] dimension and use MDX for percentage, like this:
([DIM NOS STATUSCODE].[Status].&[Failed],[Measures].[CNT HITS]) / [Measures].[CNT HITS]

It will involve a certain amount of hard coding - although you could add these measures into your cube script.
WITH
MEMBER [Measures].[failed_percent] AS
DIVIDE(
(
[DIM NOS STATUSCODE].[Status].&[Failed]
,[DIM NOS HOST].[HOST].currentmember
,[Measures].[CNT HITS]
)
, (
[DIM NOS STATUSCODE].[Status].[All]
,[DIM NOS HOST].[HOST].currentmember
,[Measures].[CNT HITS]
)
)
SELECT
NON EMPTY
{
[Measures].[CNT HITS]
,[Measures].[failed_percent]
} ON COLUMNS,
NON EMPTY
[DIM NOS HOST].[HOST].[HOST].ALLMEMBERS
ON ROWS
FROM [DW];

Related

MDX NonEmptyCrossJoin not returning expected results

I am trying to use NonEmptyCrossJoin combined with Extract to return only Members of a given dimension that are relevant. However NonEmptyCrossJoin is not returning the Non Empty tuples.
The following query returns data for the shown EOCs and Index 2.
SELECT
{ [Measures].[MTD BCWP] } ON 0,
{ CROSSJOIN([EOC].[EOC].[EOC], { [INDEX].[INDEX ID].&[2] } ) } ON 1
FROM Metrics
| | MTD BCWP
| 2 | (null)
G | 2 | 939482.280
L | 2 | 7508780.49
M | 2 | 650
O | 2 | 151652.62
Unknown | 2 | (null)
The same query using NonEmptyCrossJoin returns an empty set.
SELECT
{ [Measures].[MTD BCWP] } ON 0,
{ NONEMPTYCROSSJOIN([EOC].[EOC].[EOC], { [INDEX].[INDEX ID].&[2] } ) } ON 1
FROM Metrics
The full query that this is being used in, is much more complicated, but these examples show the crux of the issue. I believe the problem is Dimension or Measure Group configuration related, but don't know what to look for.
The EOC dimension contains only the EOC member.
The Index dimension contains Index Id(PK) and other attributes.
The Measure group includes both Index Id and EOC members that are related to their respective dimensions in addition to other values.
Thank you for your time.
You need to put your crossjoin within a nonempty() and provide the relevant measure to non-empty. The Following example will help
select
{
[Measures].[Internet Sales Amount]
}
on columns,
{
crossjoin([Product].[Subcategory].[Subcategory],{[Geography].[Country].&[United States]})
}
on rows
from
[Adventure Works]
//this results to
Now we modify our query
select
{
[Measures].[Internet Sales Amount]
}
on columns,
{
nonempty(
crossjoin([Product].[Subcategory].[Subcategory],{[Geography].[Country].&[United States]})
,
[Measures].[Internet Sales Amount])
}
on rows
from
[Adventure Works]
//This results to

MDX query and calculated members for two different averages in same query

I have an MDX/calculated member question here. It has been a while since I've done this and have forgotten a lot. I have a cube with the following dimensions and levels:
Sites
Site Name
Clients
Client Name
Industry Name
I have a measure
Product Count
What I want to show/return from an MDX query is the following:
Site | Prod Count | Avg Prod Count Across All Sites for Current Client | Avg Prod Count
Across All Sites in Current Client's Industry
Example Data:
Site | Prod Count | Avg 1 | Avg 2
Site 1 | 100 | 50 | 200
Site 2 | 125 | 50 | 200
Site 3 | 112 | 50 | 200
What I'm trying to figure out is how or if I can use 2 different calculated members to calculate the averages above.
The challenge is that the query has to be in the following format because I'm using a reporting tool and it is generating the MDX.
`SELECT
{
[Measures].[Product Count],
[Measures].[Calc Avg 1],
[Measures].[Calc Avg 2]
} ON COLUMNS,
{[Sites].[Site Name].[Site Name].Members} ON ROWS
FROM [Cube]
where ([Clients].[Client Name].&[Client A])`
So basically, my question is:
What would be the proper way to define the averages I'm looking for using calculated members?
Whenever I try it out I'm only able to calculate the average product count across all sites for the current client, but I'm not able to get the average across all sites in the current client's industry.
here's an example using adventure works to get you started. the calculated members will need to be ported to the MDX script to use with your tool. Here's the mapping:
City = "Client Site"
State = "Client"
Country = "Client Industry"
WITH
MEMBER Measures.ClientCitiesCount AS
Exists(
[Customer].[City].[City] // represents client sites
,[Customer].[State-Province].CurrentMember // represents client
).Count
MEMBER Measures.ClientCitiesSales AS
SUM(
[Customer].[State-Province].CurrentMember
,[Measures].[Internet Sales Amount]
)
MEMBER Measures.AvgAcrossClientCities AS
ClientCitiesSales/ClientCitiesCount
MEMBER Measures.IndustryCitiesCount AS
Exists(
[Customer].[City].[City] // represents industry sites
,Exists(
[Customer].[Country].[Country] // represents client's industry
,[Customer].[State-Province].CurrentMember // represents client
)
).Count
MEMBER Measures.IndustryCitiesSales AS
SUM(
Exists(
[Customer].[Country].[Country]
,[Customer].[State-Province].CurrentMember
)
,[Measures].[Internet Sales Amount]
)
MEMBER Measures.AvgAcrossIndustryCities AS
IndustryCitiesSales/IndustryCitiesCount
SELECT
{
[Measures].[Internet Sales Amount]
,ClientCitiesCount
,ClientCitiesSales
,AvgAcrossClientCities
,IndustryCitiesCount
,IndustryCitiesSales
,AvgAcrossIndustryCities
} ON 0,
{
[Customer].[City].[City] // represents client sites
} ON 1
FROM
[Adventure Works]
WHERE
[Customer].[State-Province].&[GA]&[US] // represents client
Don't forget to add in some edge-case handling (e.g. IIF the client has 0 "sites" in context) and consider using the "measuregroup" parameter in the EXISTS function.

Showing both selected Dimensions in the MDX query result?

How can I get the MDX query below to display the date next to the customer? Currently it only displays the customer, or whichever dimension I make first in the nonempty call. I'd like both to show in the results.
SELECT {[Measures].[Count1], [Measures].[Count2],
[Measures].[Count3]} ON COLUMNS,
nonempty({[Customers]}, {[DateRange]}) ON Rows
FROM Cube
I tried crossjoin but that return an out of memory error.
Thanks!
I am getting this:
Customer | Count1 | Count2 | Count3 |
I would like to see this:
Customer | Date | Count1 | Count2 | Count3 |
Generally a measure goes as the second argument of NONEMPTY
SELECT {[Measures].[Count1], [Measures].[Count2],
[Measures].[Count3]} ON COLUMNS,
nonempty(({[Customers]}, {[DateRange]}),{[Measures].[Count1], [Measures].[Count2],
[Measures].[Count3]}) ON Rows
FROM Cube
I put all three measures as part of the set for the NonEmpty function. This would return data where any row has at least one of the three counts that isn't null. It only filters out rows that are null for all three counts.

MDX query to compare different years

I am new to MDX and wondered if it is possible to create a query that shows a Sales amount per Year and per Month on two different axes, even if the sales date is a single dimension.
Something like:
Sales | 2010 | 2011 | Diff
---------+-----------+------------+----------
Jan | 1234,00 | 2345,10 | +80%
Feb | ...
...
EDIT: Added mondrian to tags, because there seem to be possibilities with other MDX implementations not available in mondrian.
Yes the solution is around calculated members :
Let's imagine your initial MDX looks like :
Select
{ [Calendar].[Year].[2010],[Calendar].[Year].[2011] } on 0,
{ [Calendar].[Months].members } on 1
from [Cube]
You can add a calculated member in the [Year] hierarchy :
With
Member [Calendar].[Year].[Diff] as [Calendar].[Year].[2011] / [Calendar].[Year].[2010]-1, FORMAT_STRING='percent'
Select
{ [Calendar].[Year].[2010],[Calendar].[Year].[2011], [Calendar].[Year].[Diff] } on 0,
{ [Calendar].[Months].members } on 1
from [Cube]
You can also add a more elegant and flexible solution, by using utility or statistical dimensions. Those dimension instead of holding data define transformations / functions and the output will not be exactly the one you're looking, but it's an interesting concept.
The problem can be solved with the ParallelPeriod function:
WITH MEMBER [Measures].[Einheiten Vorjahr]
AS '(ParallelPeriod([Year],1),
[Measures].[quantity])'
SELECT {[Measures].[quantity],[Measures].[Einheiten Vorjahr]} ON COLUMNS,
[date].[2010].children on rows
FROM salesorderitems

MDX calculation has wrong order of precendence

Im having an issue with an MDX query, and I think it boils down to the order of precedence between calculating an aggregate and a calculated member.
Let me start with the underlying data, which revolves around a valuation (which has a date, and some other data such as a member type, a scheme - and crucially for this question; a loading factor) and an associated value.
The data
Valuation Table
Id | Valuation Date | Member Type | Scheme | Loading Factor
=============================================================
1 | 2010-01-01 | TypeA | Scheme X | 0.02
2 | 2010-01-01 | TypeB | Scheme X | 0.02
3 | 2010-01-01 | TypeA | Scheme Y | 0.02
4 | 2010-01-01 | TypeB | Scheme Y | 0.02
ValuationValue table
ValuationId | Value
====================
1 | 1000.0
2 | 2000.0
3 | 3000.0
4 | 4000.0
This, when loaded into a cube has a Valuation dimension with attributes MemberType, Scheme and date. And a cube with Measure group ValuationValue containing Value measure, and a Valuation measure group containing Loading Factor like so:
Cube
-Measure Groups
- Valuation
|_Loading Factor
- ValuationValue
|_Value
- Dimensions
- Valuation
|_MemberType
|_Scheme
|_Date
The question
Loading factor is used to load the Value, think of it like a tax, so 0.02 means "Loading amount is 2% of the value". When returning Value from a query, I need to also calculate the amount to load this value by. A typical query might look like
SELECT
{
[Measures].[Value]
} ON 0,
[Valuation].[Scheme] ON 1
FROM Cube
This would return 2 rows, and as you can see by comparing to the data above it correctly sums across memberType:
Scheme | Value
=================
Scheme X | 3000.0
Scheme Y | 7000.0
Now, if I try to calculate my loading factor in that query, all goes wrong - i'll demonstrate. Given the following query:
WITH MEMBER [Measures].[Loading Value]
AS
(
[Measures].[Value] * [Measures].[Loading Factor]
)
SELECT
{
[Measures].[Value] ,
[Measures].[Loading Value]
} ON 0,
[Valuation].[Scheme] ON 1
FROM Cube
I get the result
Scheme | Value | Loading Value
=================================
Scheme X | 3000.0 | 120.0
Scheme Y | 7000.0 | 280.0
Basically, what is happening is that it is suming my Loading Factor and then multiplying that by the Sum of my values(The first row above should be 1000 * 0.02 + 2000 * 0.02 = 60. Instead it's calculating 3000 * 0.04 = 120).
This is of course a contrived example, my actual structure is a bit more complex - but I think this demonstrates the problem. I was under the impression that the calculated member in the example above should occur on a row-by-row basis, instead of at the end of an aggration of my Value measure.
Thanks for any replies.
Your [Measures].[Loading Factor] - How is that set, is it a SUM?
Calculated members are generally done as per the rows returned if I remember - Unless you specify otherwise.
If you want an example, take a look at the currency conversion wizard output - This does something similar using the LEAVES command - You will need to do this in the MDX script as a SCOPE'd command though.
Given your description, the code could be something like:
CREATE MEMBER [Measures].[Loading Value] AS NULL
Scope( { [Measures].[Loading Value] } );
Scope( Leaves([Valuation]) );
This = [Measures].[Value] * [Measures].[Loading Factor]
Format_String(This) = "#,##0.00;-#,##0.00";
End Scope;
End Scope;
I'm not sure I follow your example completely, but you might try using SOLVE_ORDER and SCOPE_ISOLATION to manipulate the order of the calculations.
For example,
WITH
MEMBER [Measures].[Custom Calculation] AS
'([Measures].[Sales Count] - [Measures].[Unit Returns])',
SOLVE_ORDER = 65535, SCOPE_ISOLATION = CUBE
SELECT
{[Measures].[Custom Calculation]} ON COLUMNS,
NON EMPTY [Time].[YQMD].[Day].AllMembers ON ROWS
FROM [Waremart]
Thes one turned out ot be REALLY easy.
WITH MEMBER [Measures].[Loading Value]
AS
(
[Measures].[Value] * [Measures].[Loading Factor]
)
WITH MEMBER [Measures].[Total Loading Value]
AS
SUM (
EXISTING [Valuation].[Id].[Id],
[Measures].[Loading Value]
)
SELECT
{
[Measures].[Value] ,
[Measures].[Measures].[Total Loading Value]
} ON 0,
[Valuation].[Scheme] ON 1
FROM Cube