ParallelPeriod issues when time is not on axis - ssas

What's wrong with the calculated member [Amount LM] (=amount last month) in this query? It seems to work if I specify the time on rows but fails if I don't, even though I have a date in the where clause.
WITH
MEMBER [Amount LM] as
(
[Measures].[Amount],
ParallelPeriod (
[Date].[Year - Quarter - Month - Date - Hierarchy].[Year and Month],
1,
[Date].[Year - Quarter - Month - Date - Hierarchy].CurrentMember
)
)
SELECT
{[Amount LM], [Measures].[Amount]} ON COLUMNS,
[Measure Types].[Name].&[22] --not related to [Measures].[Amount]; Used as a measure switch in the real case but adjusted here for simplicity
-- comment out the next line to fail
* [Date].[Year - Quarter - Month - Date - Hierarchy].[Year and Month]
ON rows
FROM [Cube]
WHERE [Date].[Year Number].&[2019]
;
Result in the working case:
Amount LM | Amount
Costs | Jan 2019 | (null) | 109600
Costs | Feb 2019 | 109600 | 218300.5
Costs | Mar 2019 | 218300.5 | 392250
Costs | Apr 2019 | 392250 | 206800
Costs | May 2019 | 206800 | 174700
Costs | Jun 2019 | 174700 | 298400
Costs | Jul 2019 | 298400 | 264550
Costs | Aug 2019 | 264550 | 424100
Costs | Sep 2019 | 424100 | 129650
Costs | Oct 2019 | 129650 | 330050
Costs | Nov 2019 | 330050 | (null)
Costs | Dec 2019 | (null) | (null)
Without the
* [Date].[Year - Quarter - Month - Date - Hierarchy].[Year and Month]
part, Amount LM ist NULL
|Amount LM| Amount
Costs | (null) | 2548400.5

There is no issue with your calculation. To understand the Null you need to know
How ParallelPeriod works
How "CurrentMember" on a UserHierarchy works
When you have
"[Date].[Year - Quarter - Month - Date - Hierarchy].[Year and Month]"
in your row axis
"[Date].[Year - Quarter - Month - Date - Hierarchy].CurrentMember"
The above ".currentmember" expression returns the current member of the USER HIERARCHY "[Year - Quarter - Month - Date - Hierarchy]". The PARALLEL PERIOD function then tries to find the cousin member with respect to LEVEL "[Year and Month]".
When you remove the user hierarchy from rows , the currentmember returns 2019, which is a member of LEVEL "YEAR Number" hence the parallel period cannot find a cousin with respect to LEVEL "[Year and Month]"(since the passed currentmember is above the LEVEL specified). To check replace the year in the where with a month member(and no date on rows) it will work. To solve the issue , you need to provide an alternate logic incase if a higher level member is passed or other edge cases use the script below it will return 100 in any edge case .
WITH
MEMBER [Amount LM] as
case when [Date].[Year - Quarter - Month - Date - Hierarchy].CurrentMember.level.name <>"Year and Month" then 100
else
(
[Measures].[Amount],
ParallelPeriod (
[Date].[Year - Quarter - Month - Date - Hierarchy].[Year and Month],
1,
[Date].[Year - Quarter - Month - Date - Hierarchy].CurrentMember
)
)
end
Below is an example of your problem on Adventure works.
I have added two columns to check what ParallelPeriod and .currentmember are return
WITH
MEMBER [Amount LM] as
(
[Measures].[Internet Sales Amount],
ParallelPeriod ([Date].[Calendar].[Month],1,[Date].[Calendar].currentmember)
)
MEMBER [Amount LM_ParallelPeriodName] as
(
ParallelPeriod ([Date].[Calendar].[Month],1,[Date].[Calendar].currentmember).name
)
MEMBER [Amount LM_CalendarCurrentmember] as
(
[Date].[Calendar].currentmember.name
)
SELECT
{[Amount LM], [Measures].[Internet Sales Amount],[Amount LM_ParallelPeriodName],[Amount LM_CalendarCurrentmember]} ON COLUMNS,
[Product].[Category].&[1] * [Date].[Calendar].[Month]
ON rows
from [Adventure Works]
WHERE [Date].[Calendar Year].&[2013]
Result
Now Lets comment the
"[Date].[Calendar].[Month]"
WITH
MEMBER [Amount LM] as
(
[Measures].[Internet Sales Amount],
ParallelPeriod ([Date].[Calendar].[Month],1,[Date].[Calendar].currentmember)
)
MEMBER [Amount LM_ParallelPeriodName] as
(
ParallelPeriod ([Date].[Calendar].[Month],1,[Date].[Calendar].currentmember).name
)
MEMBER [Amount LM_CalendarCurrentmember] as
(
[Date].[Calendar].currentmember.name
)
SELECT
{[Amount LM], [Measures].[Internet Sales Amount],[Amount LM_ParallelPeriodName],[Amount LM_CalendarCurrentmember]} ON COLUMNS,
[Product].[Category].&[1] --* [Date].[Calendar].[Month]
ON rows
from [Adventure Works]
WHERE [Date].[Calendar Year].&[2013]
Result
Notice the Null returned by ParallelPeriod, Now lets replace the Year in where from Month. This enables paralellperiod to return something useful
WITH
MEMBER [Amount LM] as
(
[Measures].[Internet Sales Amount],
ParallelPeriod ([Date].[Calendar].[Month],1,[Date].[Calendar].currentmember)
)
MEMBER [Amount LM_ParallelPeriodName] as
(
ParallelPeriod ([Date].[Calendar].[Month],1,[Date].[Calendar].currentmember).name
)
MEMBER [Amount LM_CalendarCurrentmember] as
(
[Date].[Calendar].currentmember.name
)
SELECT
{[Amount LM], [Measures].[Internet Sales Amount],[Amount LM_ParallelPeriodName],[Amount LM_CalendarCurrentmember]} ON COLUMNS,
[Product].[Category].&[1] * [Date].[Calendar].[Month]
ON rows
from [Adventure Works]
WHERE [Date].[Month Name].[September 2013]
Result

Related

Calculate measure across different time dimension tange

I have a calculated measure [Measures].[Person Count] defined as DISTINCTCOUNT(Visits[Person ID]). Our fiscal year runs from July to June. I would like to pull a report showing, by fiscal month, both distinct person counts for that month as well as a running total. However, the running total also needs to take into account distinct values. Here is what I have that almost works:
WITH MEMBER [Measures].[Cumulative Person Count] AS
Sum(PeriodsToDate([Date].[Fiscal Calendar].[Fiscal Year],
[Date].[Fiscal Calendar].CurrentMember),
[Measures].[Person Count])
SELECT [Date].[Fiscal Calendar].[Month].MEMBERS ON COLUMNS,
{ [Measures].[Person Count],
[Measures].[Cumulative Person Count] } ON ROWS
FROM [Model]
WHERE [Date].[FiscalYear].&[2018]
This gives something like the following:
+-------------------------+-------+--------+-----------+...
| | July | August | September |...
+-------------------------+-------+--------+-----------+...
| Person Count | 34268 | 37270 | 35971 |...
| Cumulative Person Count | 34268 | 71538 | 107509 |...
+-------------------------+-------+--------+-----------+...
The "Person Count" numbers are correct. The "Cumulative Person Count", however, is merely summing up the totals from each month, ignorant of the duplicates. Suppose that only 10% of person records are new each month. The result I would like to see would be something like the following:
+-------------------------+-------+--------+-----------+...
| | July | August | September |...
+-------------------------+-------+--------+-----------+...
| Person Count | 34268 | 37270 | 35971 |...
| Cumulative Person Count | 34268 | 37995 | 41592 |...
+-------------------------+-------+--------+-----------+...
What is the best way to reuse the [Measures].[Person Count] measure but apply it to a different range that is aware of the context?
You might just be using the incorrect aggregation type - try AGGREGATE instead:
WITH MEMBER [Measures].[Cumulative Person Count] AS
AGGREGATE(
PeriodsToDate(
[Date].[Fiscal Calendar].[Fiscal Year],
[Date].[Fiscal Calendar].CurrentMember),
[Measures].[Person Count]
)
An alternative to PeriodsToDate may be the use of NULL:..:
AGGREGATE(
{ NULL : [Date].[Fiscal Calendar].CurrentMember }
,[Measures].[Person Count]
)

Group Data Within Query

I wrote this query and the results are not being grouped. They are appearing as such:
Facility | Posting Month | Account Type | Amount
-------------------------------------------------
Name | July | Debit Adj. | 100
Name | July | Credit Adj. | -200
Name | July | Debit Adj. | 150
Name | July | Credit Adj. | -150
-------------------------------------------------
The results I am trying to get is the Posting month to appear once for the Debit Adj. and Credit Adj. type and the respective total amounts. Total of $250 for Debits and -$350 for credits. Thanks for the help!
SELECT pw.Facility, DATENAME(mm, pw.[Posting Date]) AS [Posting Month],
lc.[Gl Account Type], sum(pw.[Tx Amt]) AS Amount
FROM DBO.PaymentWOLedger AS pw
JOIN DBO.LedgerCodeTable AS lc
ON lc.[Description] = pw.[Tx Desc]
WHERE lc.[Gl Account Type] = 'Credit Adjustment (Write Off)' OR
lc.[GlAccount Type] = 'Debit Adjustment (CWO)'
GROUP BY pw.[Posting Date], pw.Facility, lc.[Gl Account Type]
ORDER BY pw.Facility, pw.[Posting Date]
Use Month instead of Date in Group by & Order by
GROUP BY DATENAME(mm, pw.[Posting Date]), pw.Facility, lc.[Gl Account Type]
ORDER BY pw.Facility, DATENAME(mm, pw.[Posting Date])
currently the data is grouped on each date since you have used pw.[Posting Date] in Group by which has date/month/year part in it.

Getting Order Cost from Order Line in MDX

In my cube I have a Fact Order Line, which has variable Order Cost. This variable is for course unique per Order and has the same value in every Order Line of an Order.
Now I want to create a calculated field that sums the Order Cost, but only takes this value once for every order.
So, using the calculated member this
+-------------------+--------------+------------+
| Order Line Number | Order Number | Order Line |
| | | Order Cost |
+-------------------+--------------+------------+
| 10 | 1 | $0.20 |
| 11 | 1 | $0.20 |
| 20 | 2 | $0.25 |
+-------------------+--------------+------------+
has to become this
+--------------+------------+
| Order Number | Order Cost |
+--------------+------------+
| 1 | $0.20 |
| 2 | $0.25 |
+--------------+------------+
The MDX expression I currently have (see below), sums over the order line, making the Order Cost $0.40 for Order Number 1.
SUM(
DISTINCT(
CROSSJOIN(
[Order Line Details].[Order Number].[All].Children, [Measures].[Order Line Order Cost]
)
)
)
What do I need to change to get the desired behavior?
Please let me know if there is anything unclear regarding the question.
Solution
Ok, I found the problem. I changed the Aggregate Behaviour from the [Measures].[Order Line Order Cost] to min. After that you initial solution worked. Thanks for the help!
Does the below work? I have got rid of unnecessary crossjoin, and put the distinct function on the [Order Line Details].[Order Number].Children and used the SUM function to add up the Order Line Order Cost against the order numbers.
SUM(
DISTINCT([Order Line Details].[Order Number].Children)
, [Measures].[Order Line Order Cost]
)
EDIT
Try the below code:
WITH
SET DistinctOrderNumbers AS
DISTINCT(EXISTING [Order Line Details].[Order Number].Children)
MEMBER [Measures].[Order Cost] AS
SUM(DistinctOrderNumbers, [Measures].[Order Line Order Cost])
SELECT NON EMPTY { [Measures].[Order Cost] } ON COLUMNS,
NON EMPTY { ([Reseller].[Reseller].[Reseller].ALLMEMBERS ) } ON ROWS
FROM [BI Cube]
EDIT2 (avg not sum)
WITH
SET DistinctOrderNumbers AS
DISTINCT(EXISTING [Order Line Details].[Order Number].Children)
MEMBER [Measures].[Order Cost] AS
AVG(DistinctOrderNumbers, [Measures].[Order Line Order Cost])
SELECT NON EMPTY { [Measures].[Order Cost] } ON COLUMNS,
NON EMPTY { ([Reseller].[Reseller].[Reseller].ALLMEMBERS ) } ON ROWS
FROM [BI Cube]
Please try the below:
MIN(
[Order Line Details].[Order Number].[All].Children
,[Measures].[Order Line Order Cost]
)

MDX: Aggregate [Date].[Calendar].[Quarter] level to create new [Date].[Calendar].[Semi Annual] Level

I have a Date Dimension that has Month Hierarchy
Year -> Quarter -> Month -> Day -> Hour
I have two measures. Measure_A (Aggregation is Max), Measure_B (Aggregation is SUM)
I need to Multiply Measure_A by 24 on "Day" level then I need to SUM it Semi Annually.
Is it possible to create a new hierarchy by just using an MDX query? Below is the output that i am trying to achieve
Year | CY H1 | Measure_C | Measure_B
Where: Measure_C is the SUM of (Measure_A * 24) from January 1 to June 30, and CY H1 will be the new level.
I don't have any problem with Measure_B because its aggregation is SUM. Only with Measure_A beacause the aggregation is Max.
Is this possible to achieve without altering the cube, just do an MDX? Thanks.
Updated:
This is my query and output as of the moment,
Query:
With
MEMBER [Measures].[Measure_C] AS
SUM ( Descendants ( [Date].[Date Calendar], [Date].[Date Calendar].[Day]), [Measures].[Measure_A] * 24 )
, Format_String = "#,##0"
Select
{
[Measures].[Measure_C]
} ON Columns,
NON Empty
{
[Date].[Date Calendar].[Quarter].ALLMEMBERS
} ON Rows
From [Cube]
Output:
+------+---------+-----------+
| Year | Quarter | Measure_C |
+------+---------+-----------+
| 2011 | Q1 | 12 |
| 2011 | Q2 | 45 |
| 2011 | Q3 | 12 |
| 2011 | Q4 | 25 |
+------+---------+-----------+
What I am trying to do is to merge Q1 and Q2 to get Calendar Semester for the first half (CY H1)
Required output:
+------+---------+-----------+
| Year | Half Yr | Measure_C |
+------+---------+-----------+
| 2011 | CY H1 | 57 |
| 2011 | CY H2 | 37 |
+------+---------+-----------+
Please note that we don't have a natural Hierarchy for CY H1. But according to the comment below, this is not possible using mdx alone.
By the way, I am doing this for reporting services so I guess I will just do the magic in the presentation layer since this is not achievable using MDX query alone. :)
Assuming that your year 2011 is named [Date].[Date Calendar].[2011], then you can add calculated members as children of it like this:
With
MEMBER [Measures].[Measure_C] AS
Aggregate ( Descendants ( [Date].[Date Calendar], [Date].[Date Calendar].[Day]),
[Measures].[Measure_A] * 24
)
, Format_String = "#,##0"
MEMBER [Date].[Date Calendar].[2011].[CY H1] AS
Aggregate({ [Date].[Date Calendar].[Q1], [Date].[Date Calendar].[Q2] })
MEMBER [Date].[Date Calendar].[2011].[CY H2] AS
Aggregate({ [Date].[Date Calendar].[Q3], [Date].[Date Calendar].[Q4] })
Select
{
[Measures].[Measure_C]
} ON Columns,
NON Empty
{
[Date].[Date Calendar].[2011].[CY H1],
[Date].[Date Calendar].[2011].[CY H2]
} ON Rows
From [Cube]
You can try defining something like
With Member [Measures].[Measure C] as Sum( [Date].[2014].[1].[1].[1] : [Date].[2014].[2].[6].[30] ), [Measures].[Measure A] ) * 24
Alternatively, you can define it in a more flexible way, such as
Member [Measures].[Measure C pre] as Sum( Descendants( [Date].CurrentMember, [Date].[Day] ), [Measures].[Measure A] ),
which is the sum of the max of each day, but this is only valid for Date members that exist in the dimension (not calculated members). You will then need to explicitly calculate the aggregation using the same set of months or quarters you are using in your Rows clause.

MDX: Count number of days excluding weekends

I already had an MDX query that returns number of days in a Month Hierarchy using a calculated member. What I needed to do is to add new calculated member that returns number of days excluding weekends (Saturdays and Sundays).
Below is my existing MDX query using Adventure Works DW 2008R2 SE
WITH
MEMBER [Measures].[Num of Days] AS
COUNT (
Descendants ( [Date].[Calendar], [Date].[Calendar].[Date])
)
SELECT
{ [Measures].[Num of Days] } ON COLUMNS,
[Date].[Calendar].[Month].ALLMEMBERS ON ROWS
FROM [Adventure Works]
sample existing output:
+----------------+-------------+
| Month Year | Num of Days |
+----------------+-------------+
| July 2005 | 31 |
| August 2005 | 31 |
| September 2005 | 30 |
+----------------+-------------+
sample expected output:
+----------------+-------------+--------------------------------+
| Month Year | Num of Days | Num of Days Excluding Weekends |
+----------------+-------------+--------------------------------+
| July 2005 | 31 | 21 |
| August 2005 | 31 | 23 |
| September 2005 | 30 | 22 |
+----------------+-------------+--------------------------------+
The standard solution for this is to have a hierarchy in your time dimension with the days of the week ( Monday,... Sunday). Once you for this you can filter directly using those members. You can even do a hierarchy with just weekend/no weekend. The best solution depends on the measures you've in your schema, the best is always using standard MDX aggregations and avoiding using SUM/COUNT on a set of members.
Other solutions might depend on how you implemented your hierarchy, you can use the Weekday and filter function.
Honestly something like, I haven't check it
Count( FILTER( Descendants ( [Date].[Calendar], [Date].[Calendar].[Date]) , WeekDay( [Date].[Calendar].currentmember.properties("KEY"), 2) <= 5 ) )