Slow MDX count when expanding query - mdx

Trying to create a calculated measure to be used in a KPI. The measure should count all the calls (servicedesk tickets) that have a runtime larger than 7 days.
Tested the measure with this query, which runs fast (< 3 seconds).
WITH MEMBER [Measures].[Count of Calls long runtime] AS
Count(
Filter(
[Call Details].[Call Number].MEMBERS
, [Measures].[Closed Call Run Time (days)] > 7
)
)
SELECT
{
[Count of Calls long runtime]
} ON 0
FROM
[Business Intelligence]
However when adding other members to the query, suddenly it takes forever to complete:
WITH MEMBER [Measures].[Count of Calls long runtime] AS
Count(
Filter(
(
{
[Ipc Categorisation].[Categorisation].[Subcategory].&[222]
,[Ipc Categorisation].[Categorisation].[Subcategory].&[484]
}
, [Call Details].[Call Number].[Call Number].MEMBERS
)
, [Measures].[Closed Call Run Time (days)] > 7
)
)
SELECT
{
[Count of Calls long runtime]
} ON 0
,
{
[Customer].[Customer].[Customer]
} ON 1
FROM
[Business Intelligence]
WHERE
[Date].[Month Calendar].[Year].&[2016]
There should be about 40 of 30000 calls showing up, divided over 50 customers.
When I change [Count of Calls long runtime] to another measure (calculated or in fact table), the query runs fast.
I would like to understand why this is happening. What can I do to fix this?

FILTER is at best avoided. It forces calculations to run in cell by cell mode. Let's try to change the calculation to run in block computation mode:
WITH MEMBER [Measures].IsGrt7 AS
IIF
(
[Measures].[Closed Call Run Time (days)] > 7,
1,
NULL
)
MEMBER [Measures].[Count of Calls long runtime] AS
SUM
(
{
[Ipc Categorisation].[Categorisation].[Subcategory].&[222]
,[Ipc Categorisation].[Categorisation].[Subcategory].&[484]
} * [Call Details].[Call Number].[Call Number].MEMBERS
,
[Measures].IsGrt7
)
To read more on this subject, see here.

Also worth trying feeding it into a nonempty named set before hitting the caculation:
WITH
SET [CallNonEmpty] AS
NONEMPTY(
{
[Ipc Categorisation].[Categorisation].[Subcategory].&[222]
,[Ipc Categorisation].[Categorisation].[Subcategory].&[484]
}
*[Call Details].[Call Number].[Call Number].MEMBERS
, [Measures].[Closed Call Run Time (days)]
)
MEMBER [Measures].[Count of Calls long runtime] AS
SUM(
EXISTING [CallNonEmpty],
IIF(
[Measures].[Closed Call Run Time (days)] > 7
,1
,NULL
)
)
SELECT
[Count of Calls long runtime] ON 0,
[Customer].[Customer].[Customer] ON 1
FROM [Business Intelligence]
WHERE
[Date].[Month Calendar].[Year].&[2016];

Related

MDX query runs for more than an hour

I am new to MDX queries. I have written a query which uses lead function to get values for (Current Week plus 1) as a new column for each of the metrics. Basically comparing current week value with last week's value. Without the new members the query runs within seconds. After adding the new members it runs forever. Pls suggest ways to optimize this query
Thanks for help.
WITH
SET [Range] as strtomember
(" [Time].[Week].&["+ Format(DateAdd('d', - DatePart('w', Now(), 2), Now()+7), "yyyy-MM-ddT00:00:00")
+"]")
:strtomember
(" [Time].[Week].&["+ Format(DateAdd('d', - DatePart('w', Now(), 2), Now()+14), "yyyy-MM-ddT00:00:00")
+"]")
MEMBER [Measures].[SalesNew] as
CASE WHEN [Time].[Year].CURRENTMEMBER IS [Time].[Year].&[2019] THEN
sum([Time].[Day].CURRENTMEMBER.LEAD(7),[Measures].[Sales Prev])
ELSE null
END,FORMAT_STRING = "$#,###.00"
MEMBER [Measures].[Order UnitsNew] as
CASE WHEN [Time].[Year].CURRENTMEMBER IS [Time].[Year].&[2019] THEN
sum([Time].[Day].CURRENTMEMBER.LEAD(7),[Measures].[Order Units Prev])
ELSE null
END,FORMAT_STRING = "#,##0"
MEMBER [Measures].[Count of OrdersNew] as
CASE WHEN [Time].[Year].CURRENTMEMBER IS [Time].[Year].&[2019] THEN
sum([Time].[Day].CURRENTMEMBER.LEAD(7),[Measures].[Count of Orders Prev])
ELSE null
END,FORMAT_STRING = "#,##0"
SELECT
{ [Measures].[Sales],[Measures].[Sales Prev],[Measures].[SalesNew],[Measures].[Order Units],[Measures].[Order Units Prev],
[Measures].[Order UnitsNew], [Measures].[Count of Orders],[Measures].[Count of Orders Prev], [Measures].[Count of OrdersNew]} ON COLUMNS ,
[Range] *
[Time].[Day].[Day].ALLMEMBERS * -- 4
[Time].[Hour].ALLMEMBERS *
[Product].[Merch Dept].ALLMEMBERS *
[Product].[Class].ALLMEMBERS ON ROWS
FROM [Cube]
Your MDX looks pretty cool.
Optimizing MDX is a little bit black magic - you need to just try alternatives.
Initially I'd try:
1.Swapping in strToSet rather than the two strToMember functions.
2.Do you need the condition [Time].[Year].CURRENTMEMBER IS [Time].[Year].&[2019] ? Reason I ask is that [Range] seems to
already be 2019 and you cross-join to that set so do you need to
worry about 2019?
3.You can use a simple Tuple instead of the aggregate SUM
4.Maybe while testing it might be worth removing the format_strings - shouldn't make a difference but if so you can add back.
I've not tested the following so please excuse typos:
WITH
SET [DateSet] AS
strtoSet
(
"[Time].[Week].&["
+ Format(DateAdd('d', - DatePart('w', Now(), 2), Now()+7), "yyyy-MM-ddT00:00:00")
+ "]:[Time].[Week].&["
+ Format(DateAdd('d', - DatePart('w', Now(), 2), Now()+14), "yyyy-MM-ddT00:00:00")
+"]"
)
MEMBER [Measures].[SalesNew] AS
(
[Time].[Day].CURRENTMEMBER.LEAD(7)
,[Measures].[Sales Prev]
)
MEMBER [Measures].[Order UnitsNew] AS
(
[Time].[Day].CURRENTMEMBER.LEAD(7)
,[Measures].[Order Units Prev]
)
MEMBER [Measures].[Count of OrdersNew] AS
(
[Time].[Day].CURRENTMEMBER.LEAD(7)
,[Measures].[Count of Orders Prev]
)
SELECT
{
[Measures].[Sales]
,[Measures].[Sales Prev]
,[Measures].[SalesNew]
,[Measures].[Order Units]
,[Measures].[Order Units Prev]
,[Measures].[Order UnitsNew]
,[Measures].[Count of Orders]
,[Measures].[Count of Orders Prev]
,[Measures].[Count of OrdersNew]
} ON COLUMNS ,
[DateSet] *
[Time].[Day].[Day].ALLMEMBERS *
[Time].[Hour].ALLMEMBERS *
[Product].[Merch Dept].ALLMEMBERS *
[Product].[Class].ALLMEMBERS ON ROWS
FROM [Cube];

MDX-Query - Query with to many rows. Set correct filter

I work on a problem with an MDX Query.
The cube contains models and serials (units) and should show all units in warranty for each year.
This is the a cube with this Dimensions/Measures:
CubeOverview
Now I would select all Serials which are in warranty for a special year.
The problem is that the whole Table v Dim Unit Model 4IB Test contains more than 50 Mio rows which results alsways to an QueryTimeout or sometimes to an MemoryException.
At the moment I have a MDX query (see below) which works if I select special model. But I need the filter to all models.
WITH
MEMBER [Measures].[QtyTotal] AS
[Measures].[QtyInWarranty] + [Measures].[QtyInExtension]
SELECT
NON EMPTY
{
[Measures].[QtyStdOut] ,[Measures].[QtyInExtension] ,[Measures].[QtyStdIn]
,[Measures].[QtyInWarranty] ,[Measures].[QtyTotal] ,[Measures].[SumStartWarranty]
} ON COLUMNS
,NON EMPTY
{
crossjoin(
[v Dim Unit Model 4IB Test].[ModelUnitMapping].[Id Unit].Members
,[Dim Country].[Id Country].[Id Country].members
,[Dim Calendar].[Calendar].[Month Name4report].members
)
} ON ROWS
FROM
(
SELECT
{
[v Dim Unit Model 4IB Test].[model no short].[Model No Short].&[SampleModel]
} ON COLUMNS
FROM
(
SELECT
{
[Dim Calendar].[Calendar].[Year].&[2015]
} ON COLUMNS
FROM [InstalledBaseCS_Serial]
)
)
Does anybody knows a tip to update the query to get all units for one year (round about 4 Mio rows)?
If you're trying to return the results to a visible grid in MDXstudio or SSMS then it may be timing out because there is quite a bit to render.
If you use OPENQUERY or the CLR OLAP Extensions then try the following:
Do not return the results to the screen but INSERT results into a table.
Simplifiy your script by taking away the custom measure. This can easily be calculated later as it is trivial: I have a feeling it is slowing down ssas.
Script
SELECT
NON EMPTY
{
[Measures].[QtyStdOut]
,[Measures].[QtyInExtension]
,[Measures].[QtyStdIn]
,[Measures].[QtyInWarranty]
,[Measures].[SumStartWarranty]
} ON 0
,NON EMPTY
[v Dim Unit Model 4IB Test].[ModelUnitMapping].[Id Unit].Members
*[Dim Country].[Id Country].[Id Country].members
*[Dim Calendar].[Calendar].[Month Name4report].members
ON 1
FROM
(
SELECT
[v Dim Unit Model 4IB Test].[model no short].[Model No Short].&[SampleModel] ON 0
FROM
(
SELECT [Dim Calendar].[Calendar].[Year].&[2015] ON 0
FROM [InstalledBaseCS_Serial]
)
);

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];

Running total only covering range of cells with data

Using the illustrated mockup
(Note not all DimB members show so the total for DimA is larger than the sum of the displayed DimB, ignore this):
Query is showing (months,revenue) on 0, (dima,dimb) on 1 from cube
I would like some assistance in creating the calculated member that would calculate the running total across "Months" here (not a real time dimension).
Earlier i was using this one, but it was wrong on so many levels:
[Measures].[Accumulated Revenue] =
IIF(
([DimMonths].[Months].CurrentMember,[Measures].[Revenue]) = 0
AND ([DimMonths].[Months].CurrentMember.NextMember,[Measures].[Revenue]) = 0
,
IIF(
AGGREGATE({
[DimMonths].[Months].CurrentMember :
[DimMonths].[Months].Parent.LastChild
},[Measures].[Revenue]) = 0
,
NULL
,
Aggregate( {
[DimMonths].[Months].Parent.FirstChild :
[DimMonths].[Months].CurrentMember.PrevMember
}
,[Measures].[Revenue])
)
,
Aggregate( {
[DimMonths].[Months].Parent.FirstChild :
[DimMonths].[Months].CurrentMember
}
,[Measures].[Revenue])
)
Sounds like a job for a "scoped assignment". Basically, using a scoped assignment, you can overwrite the value of your calculated measure along the [Member_DimA_01] slice so that these values represent a running total instead of a sum of children.
Here's a good tutorial from Chris Webb on scoped assignments to help get you started...
Edit: here's a template for the running total calculation...
WITH
SET [Months] AS
{
[Date].[Calendar Month].&[2011 - Jan]
:
[Date].[Calendar Month].&[2011 - Dec]
}
MEMBER [Measures].[Running Total] AS
SUM(
{
[Date].[Calendar Hierarchy].[Calendar Month].&[2011 - Jan]
:
[Date].[Calendar Hierarchy].CurrentMember
}
,[Measures].[Revenue]
)
SELECT
NON EMPTY{
[Months]
} ON 0,
NON EMPTY{
[DimA].[Member].Allmembers *
[DimB].[Member].Allmembers
} ON 1
FROM
[<<cube name>>]
WHERE
[Measures].[Revenue Running Total]

MDX - TopCount plus 'Other' or 'The Rest'

I have created an MDX query which calculates the TOP 10 ZipCodes (according to my Patient Stay measure) as such:
WITH
MEMBER [Discharge Date].[Y-M-D].[ Aggregation] AS 'AGGREGATE( EXISTING { [Current Month] } )', SOLVE_ORDER = 0
SELECT
NON EMPTY { [Measures].[Patient Stays] }
ON COLUMNS,
TOPCOUNT({ ORDER( HIERARCHIZE( { [Patient].[ByZipcode].[All].CHILDREN } ), ( [Measures].[Patient Stays] ), BDESC ) }, 10)
ON ROWS
FROM [Patient Stay]
WHERE ( [Discharge Date].[Y-M-D].[ Aggregation], [Facility].[ByAffiliation].CURRENTMEMBER, [Facility].[ByRegion].CURRENTMEMBER )
This query is used to populate a PerformancePoint 100% Stacked Bar chart. The client has asked that since this is a !00% based chart, we lump the rest of the zip codes into an "Other" field, such that there should be 11 values: one for each of the top 10, and an eleventh which is a sum of the remaining Zip Codes.
I am an extreme novice to MDX, but this doesn't souund like it should be impossible. Does anyone have any ideas or suggestions?
I'll do my best with untested code, so here goes:
WITH
MEMBER [Discharge Date].[Y-M-D].[ Aggregation] AS 'AGGREGATE( EXISTING { [Current Month] } )', SOLVE_ORDER = 0
SET [Top10ZipCodes] AS
(TOPCOUNT({ ORDER( HIERARCHIZE( { [Patient].[ByZipcode].[All].CHILDREN } ), ( [Measures].[Patient Stays] ), BDESC ) }, 10))
MEMBER [Patient].[ByZipCode].[OtherZipCodes] AS
(AGGREGATE({EXCEPT([Patient].[ByZipCode].Members, [Patient].[ByZipCode].[Top10ZipCodes])}))
SELECT
NON EMPTY { [Measures].[Patient Stays] }
ON COLUMNS,
{[Top10ZipCodes], [Patient].[ByZipCode].[OtherZipCodes]}
ON ROWS
FROM [Patient Stay]
WHERE ( [Discharge Date].[Y-M-D].[ Aggregation], [Facility].[ByAffiliation].CURRENTMEMBER, [Facility].[ByRegion].CURRENTMEMBER )
What this does is creates a set of your top 10 ZIP codes, and then aggregates (different than sum!!!) all the ZIP codes, with the exception of your top 10.
Also, if this is a common set (top 10 ZIP codes), you may want to make a set on the cube, where you can reuse it ad nauseum, without having to change every MDX query you have.
Cheers,
Eric