MDX - Daily chart with monthly average - mdx

I need a big help.
I am working with MDX to generate graphics in Pentaho - CDE / CTools. And I need to do a series of filters that will be set by the user. The chart is a daily progress, and I would like to add the average line, of the month. But it is not working very well.
What am I doing wrong? I do not have much knowledge with MDX. It should appear the values and repeat the average of the whole month, but it is repeating the same values.
WITH
MEMBER [Measures].[AVG] AS
AVG ([MONTH].[MONTH].CurrentMember, [Measures].[QTD])
SELECT NON EMPTY {[Measures].[QTD], [Measures].[AVG]} ON COLUMNS,
{[DATE].[DATE].Members} ON ROWS
FROM [DW20_DAY]
WHERE Crossjoin(Crossjoin(Crossjoin({[MONTH].[All MONTHs]}, {[CAUSE].[All CAUSEs]}), {[TYPE].[All TYPEs]}), {[MODEL].[All MODELs]})
With Filter:
WITH
MEMBER [Measures].[AVG] AS
AVG ([MONTH].[MONTH].CurrentMember, [Measures].[QTD])
SELECT NON EMPTY {[Measures].[QTD], [Measures].[AVG]} ON COLUMNS,
{[DATE].[DATE].Members} ON ROWS
FROM [DW20_DAY]
WHERE Crossjoin(Crossjoin(Crossjoin({[MONTH].[2017-03-01]}, {[CAUSE].[All CAUSEs]}), {[TYPE].[All TYPEs]}), {[MODEL].[All MODELs]})
Sample of data - In this case the average should be: 7.567.743
DATE QTD AVERAGE It has to be
01/11/2016 7.731.442 7.731.442 7.567.743
02/11/2016 7.973.846 7.973.846 7.567.743
03/11/2016 7.430.333 7.430.333 7.567.743
04/11/2016 7.517.061 7.517.061 7.567.743
05/11/2016 6.738.677 6.738.677 7.567.743
06/11/2016 6.796.424 6.796.424 7.567.743
07/11/2016 7.631.584 7.631.584 7.567.743
08/11/2016 7.907.649 7.907.649 7.567.743
09/11/2016 8.995.933 8.995.933 7.567.743
10/11/2016 7.444.471 7.444.471 7.567.743
11/11/2016 8.039.431 8.039.431 7.567.743
12/11/2016 7.240.583 7.240.583 7.567.743
13/11/2016 6.779.103 6.779.103 7.567.743
14/11/2016 7.648.149 7.648.149 7.567.743
15/11/2016 7.641.452 7.641.452 7.567.743
---- EDIT:
I requested the creation of the time dimension.
Now it's in the same hierarchy.
But I still can not solve it.
In this case, how would it look?
WITH MEMBER [Measures].[AVG] AS
Avg( Descendants([TIME].[MONTH].CURRENTMEMBER, [TIME].[DATE])
, [Measures].[QTDE]
)
SELECT {[Measures].[QTD], [Measures].[AVG]} ON COLUMNS,
NON EMPTY{[TIME].[DATE].MEMBERS} ON ROWS
FROM [DW20_DAY]
Thanks.

This sort of query is easily done if you have a multi-level user hierarchy within a Date dimension but you're using the following two attribute hierarchies:
[MONTH].[MONTH]
[DATE].[DATE]
Try using the currentmember against Date and then NonEmpty to get hold of the corresponding month:
NonEmpty([MONTH].[MONTH].MEMBERS,[DATE].CURRENTMEMBER)
Then you need to find the Dates related to that month to do the average over:
NonEmpty([DATE].[DATE].MEMBERS,
NonEmpty([MONTH].[MONTH].MEMBERS,[DATE].CURRENTMEMBER).ITEM(0)
)
Then try applying the Avg:
WITH
MEMBER [Measures].[AVG] AS
AVG (
NonEmpty(
[DATE].[DATE].MEMBERS,
NonEmpty(
[MONTH].[MONTH].MEMBERS,[DATE].CURRENTMEMBER).ITEM(0)
)
, [Measures].[QTD]
)
...
...
I'll need to test the above against AdvWrks!

The following code must work as well:
Member [Measures].[AVG] as
AVG([DATE].[DATE].[DATE].Members, [Measures].[QTD])
If you have a hierarchy Day -> Month and want to obtain AVG by parent, try:
AVG([DATE].[DATE].CurrentMember.Siblings, [Measures].[QTD])

Related

How to add the first of the month for gaps in dates BigQuery?

I am working on generating previous months sales so I can do an accurate MOM for two years of data.
The issue is when I try to do anything it either blocks me because datetime is a timestamp, but when I cast it then I can't call on the alias.
`
here is my code below where I have the prev_month generated and I also have the date truncated so it sums all totals by the first of that month.
SELECT
  x1,
  x2,
  SUM(amount) AS total,
LAG(SUM(total)) OVER (PARTITION BY x1, x2 ORDER BY total ASC ) as prev_total,
prev_total
FROM(
SELECT  
    t2.x1,
    t3.name,
    SUM(amount) AS total,
    DATE_TRUNC(t1.datetime, MONTH) AS total
  FROM testtable1 AS t1
  JOIN testtable2 As t2 ON op = oc
INNER testtable3 AS t3 ON t3.bc = CAST(t2.ob AS int64)
  INNER JOIN testtable4 AS t4 ON t4.pi = t3.pc
WHERE t4.pc NOT IN ('MN', 'MN2','MN3', 'OH', 'OI', 'PT', 'RT')
    AND t2.pa = 'FD'
    AND t1.datetime >='2021-01-01'
    AND t2.od <> 'PC'
  GROUP BY 
    op.o_name, 
    bh.b_name,
    pr.P_ActionDateTime
)
GROUP BY
  x1,
  x2,
  prev_total
ORDER BY 
  total DESC;
`
is there a way I can insert missing dates even though my datetime field is a timestamp?
in the example below I would want to insert the first of the month of February and record a zero dollar sale. In the data currently if there isn't any amount it doesn't record a timestamp. So when comparing the previous month it will only go to the last month that has sales.
example:
datetime total prev_total
1/1/2022 $13,000 $11,000
3/1/2022 $9,0000 $13,000
4/1/2022 $5,000 $9,000
goal:
dateime total prev_total
1/1/2022 $13,000 $11,000
2/1/2022 $0 $13,000
3/1/2022 $9,000 $0
4/1/2022 $5,000 $9,000
You need to create a CTE with all months you want to cover taking advantage of the GENERATE_DATE_ARRAY function
https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#generate_date_array
Then you can left join the CTE with your query so that months without actual value on your query can be displayed with NULL values.

MDX LAG Command in Calculated Measure return wrong value

I have following query
with member priorSellOut as
([Date].[Year Month].CurrentMember.LAG(1),MEASURES.[Sell Out])
Select
{
MEASURES.[Sell Out]
,PriorSellOut
} ON COLUMNS,
[Date].[Year Month].[Year Month].Members ON ROWS
From Sales
the result of this query is:
Sell Out Prior Sell Out
2018.01 34329 (null)
2018.02 37752 34329
2018.03 21798 37752
2018.04 41477 21798
2018.05 50125 41477
2018.06 6363 50125
2018.07 11511 6363
2018.08 7444 11511
2018.09 13989 7444
2018.10 1936 13989
I want to have last 3 month ago. and use [3 Month Ago] named set.following query:
with member priorSellOut as
([Date].[Year Month].CurrentMember.LAG(1),MEASURES.[Sell Out])
Select
{
MEASURES.[Sell Out]
,PriorSellOut
} ON COLUMNS,
[Date].[Year Month].[Year Month].Members ON ROWS
From Sales
Where ([3 Month Ago])
the result of this query is:
Sell Out Prior Sell Out
2018.08 7444 (null)
2018.09 13989 7444
2018.10 1936 13989
I want to have following result. Prior Sell Out Column for 2018.08 must have value.
Sell Out Prior Sell Out
2018.08 7444 11511
2018.09 13989 7444
2018.10 1936 13989
Thanks in advance
Your problem is that instead of filtering on the list of rows to display, you instead filter out all data that is not in the last 3 months.
To filter out the rows - just apply a limited set expression to the axis. We want the last 3 months to be displayed, so let's take the last three members of the months list like so: TAIL([Date].[Year Month].[Year Month].Members,3) . Now we can safely remove the WHERE clause. The result is:
with member priorSellOut as
([Date].[Year Month].CurrentMember.LAG(1),MEASURES.[Sell Out])
Select
{
MEASURES.[Sell Out]
,PriorSellOut
} ON COLUMNS,
TAIL([Date].[Year Month].[Year Month].Members,3) ON ROWS
From Sales
NOTE: This assumes that the [Date].[Year Month].[Year Month] dimension IS PROPERLY ORDERED
EDIT: If you really necessary need to use the [3 Month Ago] named set - just put it on the axis instead of the TAIL(...) formula I used.

MDX: Mixed row aggregation types within single period column time aggregation

I am trying wrap my head around a way to produce the following result from a Mondrian cube.
Sample Values:
Year Month Sales
---- ----- -----
2015 Jan 10
2015 Feb 11
2015 Mar 12
2015 Apr 10
2015 May 11
2015 Jun 12
Jan-Mar 2015 | Apr-Jun 2015
---------------------------------------------------
Sales Sum | 33 | 33
Sales Average | 11 | 11
The current MDX is something like this:
with
member [Date].[JAN-MAR] as Aggregate([Date].[2015].[3].lag(2):[Date].[2015].[3])
member [Date].[APR-JUN] as Aggregate([Date].[2015].[6].lag(2):[Date].[2015].[6])
member [Measures].[Sales Sum] as Sum([Date].CurrentMember, [Measures].[Sales])
member [Measures].[Sales Average] as Avg([Date].CurrentMember, [Measures].[Sales])
select
{[Date].[JAN-MAR],
[Date].[APR-JUN]} on columns,
{[Measures].[Sales Sum],
[Measures].[Sales Average]} on rows
from [Cube]
The question is how can I get a row to specify an aggregate to use for the current column period aggregation?
Update (17 Aug 2018)
I think I have found a solution, before I get into that I think I should give more background into the scenario. We are using Mondrian to provide some financial reports. Due to the complexity of the reports combined with the fact that end users must be able to create them we have created our own mini reporting tool.
One of the most common report types is measures on rows and columns with various date aggregations e.g. Three Month Rolling Average / Financial Year to Date etc all based on a report parameter date selection offset.
The complexity comes in where for the same column they want different rows to aggregate differently. An example would be the Financial Year to Date column, some rows measures must be summed, some must be averaged and some must return the closing balance.
I haven't found an easy want to model this in the cube yet :/
However I found a way to get it to work by mistake that seems relevantly robust and is also fast. As it turns out Mondrian does not validate member attributes, i.e. you can declare and reference whatever member attributes you want. This has turned out to provide an easy way to can get access to the correct date slice and perform whatever aggregate I want e.g:
with
member [Date].[JAN-MAR] as Aggregate([Date].[2015].[3].lag(2):[Date].[2015].[3]), START_MONTH_MEMBER='[Date].[2015].[1]', END_MONTH_MEMBER='[Date].[2015].[3]'
member [Date].[APR-JUN] as Aggregate([Date].[2015].[6].lag(2):[Date].[2015].[6]), START_MONTH_MEMBER='[Date].[2015].[4]', END_MONTH_MEMBER='[Date].[2015].[6]'
member [Measures].[Sales Sum] as Sum([Date].CurrentMember, [Measures].[Sales])
member [Measures].[Sales Average] as Avg(StrToMember([Date].CurrentMember.Properties('START_MONTH_MEMBER')):StrToMember([Date].CurrentMember.Properties('END_MONTH_MEMBER')), [Measures].[Sales])
select
{[Date].[JAN-MAR],
[Date].[APR-JUN]} on columns,
{[Measures].[Sales Sum],
[Measures].[Sales Average]} on rows
from [Cube]
So far this works well. One thing that doesn't work is I cannot get StrToSet to work. In theory you should be able to declare a set in the with member property and then use the in the measure.
StrToMember(([Date].CurrentMember.Properties('MONTH_RANGE_SET'))
So this what I have working for now, would love some feedback on that?
This is a bit time consuming, but should work:
with
member [Date].[JAN-MAR] as Aggregate([Date].[2015].[3].lag(2):[Date].[2015].[3])
member [Date].[APR-JUN] as Aggregate([Date].[2015].[6].lag(2):[Date].[2015].[6])
member [Measures].[Sales Sum] as Sum([Date].CurrentMember, [Measures].[Sales])
member [measures].yearvalues as [Date].currentmember.member_value
member [Measures].[Sales Average] as
AVG
(
StrToSet(
"[Date].[2015].&[" +
CASE
LEFT(measures.yearvalues, 3)
WHEN "JAN" THEN 1
WHEN "APR" THEN 4 END +
"]:[Date].[2015].&[" +
CASE
RIGHT(measures.yearvalues, 3)
WHEN "MAR" THEN 3
WHEN "JUN" THEN 5 END +
"]"
)
,
[Measures].[Sales]
),
format_string = "#.##"
select
{[Date].[JAN-MAR],
[Date].[APR-JUN]} on columns
{[Measures].[Sales Sum],
[Measures].[Sales Average]} on columns
from [Cube]
Far from ideal but best I can do at the moment:
WITH
SET [JAN-MAR] AS
[Date].[Calendar].[Month].&[2006]&[3].Lag(2)
:
[Date].[Calendar].[Month].&[2006]&[3]
SET [APR-JUN] AS
[Date].[Calendar].[Month].&[2006]&[6].Lag(2)
:
[Date].[Calendar].[Month].&[2006]&[6]
MEMBER [Date].[Calendar].[JAN-MAR] AS
Aggregate([JAN-MAR])
MEMBER [Date].[Calendar].[APR-JUN] AS
Aggregate([APR-JUN])
MEMBER [Measures].[Sales Sum] AS
[Measures].[Internet Sales Amount]
MEMBER [Measures].[Sales Average] AS
[Measures].[Internet Sales Amount] / [JAN-MAR].Count
SELECT
{
[Date].[Calendar].[JAN-MAR]
,[Date].[Calendar].[APR-JUN]
} ON 0
,{
[Measures].[Sales Sum]
,[Measures].[Sales Average]
} ON 1
FROM [Adventure Works];
So I thought maybe I'd try adding the custom members to an unrelated dimension (effectively make it a utility dimension). This works ok but extracting the count of number of related months is still proving difficult. This is the current effort:
WITH
SET [JAN-MAR] AS
[Date].[Calendar].[Month].&[2006]&[3].Lag(2)
:
[Date].[Calendar].[Month].&[2006]&[3]
SET [APR-JUN] AS
[Date].[Calendar].[Month].&[2006]&[6].Lag(2)
:
[Date].[Calendar].[Month].&[2006]&[6]
MEMBER [Product].[Category].[JAN-MAR] AS
Aggregate
(
[JAN-MAR]
,[Product].[Category].[All Products]
)
MEMBER [Product].[Category].[APR-JUN] AS
Aggregate
(
[APR-JUN]
,[Product].[Category].[All Products]
)
MEMBER [Measures].[Sales Sum] AS
[Measures].[Internet Sales Amount]
MEMBER [Measures].[Sales Avg] AS
[Measures].[Internet Sales Amount]
/
NonEmpty
(
[Date].[Calendar].[Month].MEMBERS
,(
[Product].[Category].CurrentMember
,[Measures].[Internet Sales Amount]
)
).Count //<<<<currently returning 72 rather than 3
SELECT
{
[Product].[Category].[JAN-MAR]
,[Product].[Category].[APR-JUN]
} ON 0
,{
[Measures].[Sales Sum]
,[Measures].[Sales Avg]
} ON 1
FROM [Adventure Works];
We can see that it is getting divided by 72 rather than 3:
Problem as I currently see it is trying to get hold of the number of related months to each of the custom members after they have been aggregated - here is a simplified example of what I mean:
WITH
SET [JAN-MAR] AS
//<< set of 3 months
[Date].[Calendar].[Month].&[2006]&[1]
:
[Date].[Calendar].[Month].&[2006]&[3]
MEMBER [Product].[Category].[JAN-MAR] AS
//<< chuck on unconnected hierarchy
Aggregate
(
[JAN-MAR]
,[Product].[Category].[All Products]
)
MEMBER [Measures].[countMonthsRelatedToMember] AS //<<attempt to count mths related to [Product].[Category].[JAN-MAR]
NonEmpty
(
[Date].[Calendar].[Month].MEMBERS
,(
[Product].[Category].CurrentMember
,[Measures].[Internet Sales Amount]
)
).Count //<<<<currently returning 72 rather than 3
SELECT
[Product].[Category].[JAN-MAR] ON 0
,[Measures].[countMonthsRelatedToMember] ON 1
FROM [Adventure Works];

MDX: group by with LastPeriods group by, Mondrian Schema: levelType hours

I'm new to MDX and Mondrian and have two time related questions:
1.)
The MDX command
SELECT NON EMPTY {[Country].[Country].Members} ON COLUMNS, [Time].[2012].[Q1 2012].[2].[2012-02-08]:[Time].[2012].[Q4 2012].[11].[2012-11-08] ON ROWS FROM [MyCube] WHERE {[Measures].[Sales]}
prints the result grouped by days:
2012-02-08 | 2873 | 9829 | ...
2012-02-09 | ...
But I want to define the date range in days and get the result grouped by months:
2012-02 | 34298| ...
2012-03 | ...
2.)
The Mondrian schema documentation lists the time level types TimeYears, TimeQuarters, TimeMonths and TimeDays. Is it possible to define hours too?
Thanks a lot.
1)
The range function in MDX returns members of the level you're using. In your case :
[Time].[2012].[Q1 2012].[2].[2012-02-08]:[Time].[2012].[Q4 2012].[11].[2012-11-08]
You're using days so that's why you're getting all days. Use month instead of days in your range function. In case you do not want the data before the 8th, an option would be using a subselect to filter :
SELECT
NON EMPTY {[Country].[Country].Members} ON COLUMNS,
[Time].[Your month level].members} ON ROWS
FROM (
SELECT
{[Measures].[Sales]} ON 0,
[Time].[2012].[Q1 2012].[2].[2012-02-08]:[Time].[2012].[Q4 2012].[11].[2012-11-08] ON 1,
FROM [MyCube] )
2) Don't know for Mondrian, but in any case you can create a time dimension based on an 'existing' table

MDX -No Sales over 30 days

I'd like to get the product with zero sales over 30 days. E.g. Below is my expected result:
Store,Product,Days
Store1, product1, 33
Store1, product2, 100
Store2, product5, 96
Store34, product14, 78
Store100, product9, 47
So I wrote below query:
WITH
MEMBER [Measures].[Zero Sales Days]
AS
COUNT(
FILTER(
NONEMPTY( [Calendar].[Date].[Day],[Measures].[POS Qty])
, ( [Measures].[POS Qty]=0)
)
)
SELECT
([Store].[Store].[Store],[product].[product].[product]) on 1,
([MEASURES].[Zero Sales Days]) ON 0
FROM [testcube]
The problem is: How to filter the case: days of zero sales<30
Thanks,
Nia
I did some change and then ran against my DB.
I got nothing if I added the where cause. If not, the result is '#Error'.
I need not select any time related dimension. What I want to do for the report is: select store and product dimension, and define a calculated measure to get the count. Boyan, I will be really appreciated it if you can need the detailed the query for it.
The function LastPeriods is what you're looking for:
WITH
MEMBER [Measures].[Zero Sales Days]
AS COUNT(
FILTER([Calendar].[Date].[Day],
SUM( LastPeriods(30, [Calendar].[Date].currentmember),[Measures].[POS Qty])
= 0 )
)
SELECT
([Store].[Store].[Store],[product].[product].[product]) on 1,
([MEASURES].[Zero Sales Days]) ON 0
FROM [testcube]
The following query works against Adventure Works and shows you the products with no sales for over 30 days from the date in the WHERE clause back:
WITH
MEMBER [Measures].[Number of Periods With No Sales] AS
Iif(([Date].[Date].CurrentMember, [Measures].[Internet Sales Amount])=0,
([Date].[Date].PrevMember, [Measures].[Number of Periods With No Sales])+1,
NULL
)
MEMBER [Measures].[Number of > 30 Periods With No Sales] AS
Sum(
Iif([Measures].[Number of Periods With No Sales] > 30,
[Measures].[Number of Periods With No Sales],
NULL
)
)
SELECT
{
[Measures].[Number of > 30 Periods With No Sales]
} ON 0,
NON EMPTY {
[Product].[Product Categories].[Product]
} ON 1
FROM [Adventure Works]
WHERE [Date].[Calendar].[Date].&[860]
You will need to re-work it (change the dimension/measure names) to get it to work against your db. Please let me know if you need a query which can give you all products regardless of the date, which have at least one period with more than 30 days with no sales (e.g. max period with no sales, or an arbitrary such period). This will require a few changes. Also, since the query is using recursion it may be slow - if it is too slow we can see how to improve its performance - something which may require changes to your data model to support this bit of analytics.