MDX - How to exclude a period in time relative calculation - time

I am trying to exclude the period P13 (special period at the end of the fiscal year) for some time relative calculations, like a Sum using ParallelPeriod over 12 Months, or an average using Lag().
Here is a MDX so far:
WITH MEMBER [Measures].[Sum Amount 12 Months]
AS
Sum(
ParallelPeriod( [Time].[Fiscal Calendar].[Fiscal Period],11):[Time].[Fiscal Calendar] ,
[Measures].[Amount]
)
Periods in my hierarchy are like:
2014-P1,...., 2014-P12,2014-P13,2015-P1,...
So the idea would be first to simulate a removing of all periods -P13, then aggregate... But I really need help for this...
Thanks

You can use the EXCEPT function to get rid of the P13.
WITH SET P13PeriodMember
AS TAIL(EXISTS([Time].[Fiscal Calendar].[Fiscal Period].MEMBERS, [Time].[Fiscal Calendar].[Fiscal Year].CURRENTMEMBER), 1).ITEM(0).ITEM(0)
MEMBER [Measures].[Sum Amount 12 Months]
AS
SUM(
EXCEPT(
ParallelPeriod([Time].[Fiscal Calendar].[Fiscal Period].currentmember,11):[Time].[Fiscal Calendar].[Fiscal Period].currentmember,
P13PeriodMember),
[Measures].[Amount]
)

Here is the MDX now working, Sourav_Agasti answer was really helpful, but an issue was still there concerning the ParallelPeriod parameter. Indeed, if we exclude a member from the Set we still need to increment our parameter in order to add 1 period...
There is probably an easier solution and I will be glad to hear it!
WITH
SET P13Periods
AS
{ //Exclude P13 members (hardcode for test)
[Time].[Fiscal Calendar].[Fiscal Period].&[201013],
[Time].[Fiscal Calendar].[Fiscal Period].&[201113],
[Time].[Fiscal Calendar].[Fiscal Period].&[201213],
[Time].[Fiscal Calendar].[Fiscal Period].&[201313]
}
MEMBER [Measures].[Amount Sum 2 Months]
AS
SUM(
//Check if range Period contains a P13
//If yes, then we need to exclude this period from calculation AND add 1 Period to ParallelPeriod
IIF(
EXISTS(
P13Periods,
ParallelPeriod([Time].[Fiscal Calendar].[Fiscal Period],1):[Time].[Fiscal Calendar]
).Count =1
,EXCEPT(
ParallelPeriod([Time].[Fiscal Calendar].[Fiscal Period],2):[Time].[Fiscal Calendar]
,P13Periods
)
,ParallelPeriod([Time].[Fiscal Calendar].[Fiscal Period],1):[Time].[Fiscal Calendar]
)
,
[Measures].[Amount]
)
SELECT {[Measures].[Amount],[Measures].[Amount Sum 2 Months]} on 0
,[Time].[Fiscal Calendar].[Fiscal Period] on 1
FROM [MyCube]

Related

floor while calculating between two dates is giving 0 in Oracle

I have two dates by which I am calculating no of years/months. For below 2 dates I am getting output as 0 as it should return 0.4 months.
Here is my query
select floor((months_between(to_date('2022-07-01T00:00:00+05:30'), to_date('2022-01-11T00:00:00+05:30', 'dd-mm-yy'))) /12)
from dual;
Please suggest what I am doing wrong here
The floor function:
returns the largest integer equal to or less than n
so there is no way it can return 0.4. The ceil function is the similar. Neither takes an argument allowing retention of decimal places. And you don't want to round it, as in your example that would give 0.5, not 0.4.
Fortunately you can use trunc, which does have a decimal-place argument:
The TRUNC (number) function returns n1 truncated to n2 decimal places.
So you want trunc(<difference between dates>, 1) to get retain 1 decimal place.
select trunc (
months_between(
CAST(TO_TIMESTAMP_TZ('2022-07-01T00:00:00+05:30','YYYY-MM-DD"T"HH24:MI:SSTZH:TZM') AS DATE),
CAST(TO_TIMESTAMP_TZ('2022-01-11T00:00:00+05:30','YYYY-MM-DD"T"HH24:MI:SSTZH:TZM') AS DATE)
) / 12
, 1
) as result
from dual;
.4
Here trunc behaves essentially as you would want floor(n1, n2) to if that existed; there is no equivalent for ceil, but you can work around that. The same method can be applied here too, but isn't needed; I've included it in this db<>fiddle for fun.
You want:
to use TO_TIMESTAMP_TZ and not TO_DATE
to use a format model that matches the timestamp format such as YYYY-MM-DD"T"HH24:MI:SSTZD
to use FLOOR before dividing by 12 if you want to find the number of full months.
select FLOOR(
MONTHS_BETWEEN(
to_timestamp_tz('2022-07-01T00:00:00+05:30', 'YYYY-MM-DD"T"HH24:MI:SSTZD'),
to_timestamp_tz('2022-01-11T00:00:00+05:30', 'YYYY-MM-DD"T"HH24:MI:SSTZD')
)
) / 12 AS full_months_diff
from dual;
Which outputs:
FULL_MONTHS_DIFF
.4166666666666666666666666666666666666667
Alternatively, you could use the difference between the timestamps as an INTERVAL YEAR TO MONTH data type:
select EXTRACT(
YEAR FROM
( to_timestamp_tz('2022-07-01T00:00:00+05:30', 'YYYY-MM-DD"T"HH24:MI:SSTZD')
- to_timestamp_tz('2022-01-11T00:00:00+05:30', 'YYYY-MM-DD"T"HH24:MI:SSTZD')
) YEAR TO MONTH
) AS years,
EXTRACT(
MONTH FROM
(to_timestamp_tz('2022-07-01T00:00:00+05:30', 'YYYY-MM-DD"T"HH24:MI:SSTZD')
- to_timestamp_tz('2022-01-11T00:00:00+05:30', 'YYYY-MM-DD"T"HH24:MI:SSTZD')
) YEAR TO MONTH
) AS months
from dual;
YEARS
MONTHS
0
6
Which rounds up the number of months.
db<>fiddle here

MYSQL - How do I unpivot my data to get the following output below?

Never used MYSQL unpivot much and its a complex example so thanks in advance.
I am looking to get the following query to be Unpivoted by COD_DUE_DATE. So I want the CASE_ID's down the column and along the column I want 4 parts:- Less than a week, due in a week, due in 2 week, due in 3 weeks ect.
Then the values in the middle will be a count of CASE_ID
SELECT DISTINCT a.CASE_ID, b.BUCKET, a.CUSTOMER_OCCUPATION_DATE,
CASE
WHEN a.CUSTOMER_OCCUPATION_DATE BETWEEN SYSDATE-10000 AND SYSDATE+7 THEN 'LESS THAN 1 WEEK'
WHEN a.CUSTOMER_OCCUPATION_DATE BETWEEN SYSDATE+8 AND SYSDATE+14 THEN 'DUE IN 1 WEEK'
WHEN a.CUSTOMER_OCCUPATION_DATE BETWEEN SYSDATE+15 AND SYSDATE+21 THEN 'DUE IN 2 WEEKS'
WHEN a.CUSTOMER_OCCUPATION_DATE BETWEEN SYSDATE+22 AND SYSDATE+28 THEN 'DUE IN 3 WEEKS'
ELSE 'DUE IN 4+ WEEKS'
END as COD_DUE_DATE
FROM FND_COMPLAINTS a, FTTP_NEWSITES_DWELL_DETAIL b,
unpivot
(
a.CUSTOMER_OCCUPATION_DATE
for COD_DUE_DATE in (LESS THAN 1 WEEK)
)
WHERE a.NAD_KEY = b.NAD(+) AND a.CUSTOMER_OCCUPATION_DATE IS NOT NULL
ORDER BY a.CUSTOMER_OCCUPATION_DATE ASC

Max value from Member Property

I need to get the max value of a member property to use in another MDX expression.
As an example from Adventure Works I'm using the following
WITH
MEMBER DoFP AS
[Customer].[Customer].Properties("Date of First Purchase")
MEMBER MaxDoFP AS
Tail
(
NonEmpty
(
[Date].[Date].[Date]
,[Measures].[DoFP]
)
,1
).Item(0).MemberValue
SELECT
[Date].[Calendar Year].[Calendar Year] ON 0
,{
[Customer].[Customer].&[20075]
,[Customer].[Customer].&[15568]
,[Customer].[Customer].&[20285]
} ON 1
FROM [Adventure works]
WHERE
[Measures].[DoFP];
I'd like it to return June 15, 2008 for all rows/cols which is the date of first purchase for Aaron Alexander (who has the max DoFP of the customers selected) so that I can do some more calcs. Instead its giving me 31st Dec 2010 because (I assume) that's the last date in my [Date].[Date].[Date].
Not pretty:
WITH
SET X AS
{
[Customer].[Customer].&[20075]
,[Customer].[Customer].&[15568]
,[Customer].[Customer].&[20285]
}
MEMBER DoFP AS
[Customer].[Customer].Properties("Date of First Purchase")
MEMBER MaxDoFP1 AS
Max(DoFP)
MEMBER MaxDoFP2 AS
Max
(
X
,Cdate([Customer].[Customer].Properties("Date of First Purchase"))
)
SELECT
[Date].[Calendar Year].[Calendar Year] ON 0
,
X * {[Measures].[MaxDoFP1],[Measures].[MaxDoFP2]} ON 1
FROM [Adventure works];
Another option (assuming you have the ability to extend the cube structure) would be to create a measure group based on the customer dimension using a MAX aggregation function. I imagine performance would be a bit better for larger sets.
WITH
SET X AS
{
[Customer].[Customer].&[20075]
,[Customer].[Customer].&[15568]
,[Customer].[Customer].&[20285]
}
MEMBER MaxDoFP AS
Max (X,[Measures].[Customer DoFP MAX])
SELECT
[Date].[Calendar Year].[Calendar Year] ON 0
,X * {[Measures].[MaxDoFP]} ON 1
FROM [Adventure works];

Range of Week instead of be putting dates

I have below query but instead of to be putting range of week like '22-OCT-2012 AND 28-OCT-2012' I woulk to put a code like CurrentWeek -2 or CurrentWeek-1, that will avoid to edit the query every week that I need to run it.
Do you know how make this?
tHANKS
LD
SELECT WO.USER_6 AS STYLE
,SUM (CASE WHEN (OPERATION.STATUS ='C' AND OPERATION.CLOSE_DATE BETWEEN '22-OCT-2012' AND '28-OCT-2012') THEN OPERATION.RUN_HRS ELSE 0 END) WEEK43
,SUM (CASE WHEN (OPERATION.STATUS ='C' AND OPERATION.CLOSE_DATE BETWEEN '29-OCT-2012' AND '04-NOV-2012') THEN OPERATION.RUN_HRS ELSE 0 END) WEEK44
FROM WORK_ORDER WO, OPERATION
WHERE WO.BASE_ID = OPERATION.WORKORDER_BASE_ID
AND WO.Lot_ID = Operation.Workorder_Lot_ID
AND WO.Sub_ID = Operation.Workorder_Sub_ID
AND WO.Split_ID = Operation.Workorder_Split_ID
AND WO.TYPE ='W'
AND WO.WAREHOUSE_ID ='MEX-04'
AND OPERATION.CLOSE_DATE BETWEEN '22-OCT-2012' AND '04-NOV-2012'
AND OPERATION.RESOURCE_ID IN ('171-4','171-ADD','171-3' ,'BAMEX-SEWCONC','BAMEX-SEWPATC')
AND OPERATION.RUN > 0
GROUP BY
WO.USER_6
If I understood your question then u can use this to pass the current week days like this
SUM (CASE WHEN (OPERATION.STATUS ='C' AND OPERATION.CLOSE_DATE BETWEEN to_char(trunc(sysdate),'DD-MON-YYYY') and to_char(trunc(sysdate)-6,'DD-MON-YYYY')) THEN OPERATION.RUN_HRS ELSE 0 END) WEEK43
Hope this help u
In this case I will use trunc function:
currentweek will be trunc(sysdate,'D')
current_week - 1 will be trunc(sysdate,'D') - 7
current week - 2 will be trunc(sysdate,'D') - 2 * 7
Attention this will give first day of week sunday. If you want monday you should and one day:
current week - 2 will be trunc(sysdate,'D') - 2 * 7 + 1
UPDATE:
Frank is right, the behavior on first day of week depends on NLS_TERITORY
alter session set NLS_TERRITORY ='UNITED KINGDOM';
select trunc(sysdate,'D') from dual;
05-11-2012
alter session set NLS_TERRITORY ='AMERICA';
select trunc(sysdate,'D') from dual;
04-11-2012

In Oracle, is there a function that calculates the difference between two Dates?

In Oracle, is there a function that calculates the difference between two Dates? If not, is a way to display the difference between two dates in hours and minutes?
Query:
SELECT Round(max((EndDate - StartDate ) * 24), 2) as MaximumScheduleTime,
Round(min((EndDate - StartDate) * 24), 2) as MinimumScheduleTime,
Round(avg((EndDate - StartDate) * 24), 2) as AveragegScheduleTime
FROM table1
You can subtract two dates in Oracle. The result is a FLOAT which represents the number of days between the two dates. You can do simple arithmetic on the fractional part to calculate the hours, minutes and seconds.
Here's an example:
SELECT TO_DATE('2000/01/02:12:00:00PM', 'yyyy/mm/dd:hh:mi:ssam')-TO_DATE('2000/01/01:12:00:00AM', 'yyyy/mm/dd:hh:mi:ssam') DAYS FROM DUAL
Results in: 1.5
You can use these functions :
1) EXTRACT(element FROM temporal_value)
2) NUMTOYMINTERVAL (n, unit)
3) NUMTODSINTERVAL (n, unit).
For example :
SELECT EXTRACT(DAY FROM NUMTODSINTERVAL(end_time - start_time, 'DAY'))
|| ' days ' ||
EXTRACT(HOUR FROM NUMTODSINTERVAL(end_time - start_time, 'DAY'))
||':'||
EXTRACT(MINUTE FROM NUMTODSINTERVAL(end_time - start_time, 'DAY'))
||':'||
EXTRACT(SECOND FROM NUMTODSINTERVAL(end_time - start_time, 'DAY'))
"Lead Time"
FROM table;
With Oracle Dates, this is pretty
trivial, you can get either TOTAL
(days, hours, minutes, seconds)
between 2 dates simply by subtracting
them or with a little mod'ing you can
get Days/Hours/Minutes/Seconds
between.
http://asktom.oracle.com/tkyte/Misc/DateDiff.html
Also, from the above link:
If you really want 'datediff' in your
database, you can just do something
like this:
SQL> create or replace function datediff( p_what in varchar2,
2 p_d1 in date,
3 p_d2 in date ) return number
4 as
5 l_result number;
6 begin
7 select (p_d2-p_d1) *
8 decode( upper(p_what),
9 'SS', 24*60*60, 'MI', 24*60, 'HH', 24, NULL )
10 into l_result from dual;
11
11 return l_result;
12 end;
13 /
Function created
Q: In Oracle, is there a function that calculates the difference between two Dates?
Just subtract one date expression from another to get the difference expressed as a number of days. The integer portion is the number of whole days, the fractional portion is the fraction of a day. Simple arithmetic after that, multiply by 24 to get hours.
Q: If not, is a way to display the difference between two dates in hours and minutes?
It's just a matter of expressing the duration as whole hours and remainder minutes.
We can go "old school" to get durations in hhhh:mi format using a combination of simple builtin functions:
SELECT decode(sign(t.maxst),-1,'-','')||to_char(floor(abs(t.maxst)/60))||
decode(t.maxst,null,'',':')||to_char(mod(abs(t.maxst),60),'FM00')
as MaximumScheduleTime
, decode(sign(t.minst),-1,'-','')||to_char(floor(abs(t.minst)/60))||
decode(t.minst,null,'',':')||to_char(mod(abs(t.minst),60),'FM00')
as MinimumScheduleTime
, decode(sign(t.avgst),-1,'-','')||to_char(floor(abs(t.avgst)/60))
decode(t.avgst,null,'',':')||to_char(mod(abs(t.avgst),60),'FM00')
as AverageScheduleTime
FROM (
SELECT round(max((EndDate - StartDate) *1440),0) as maxst
, round(min((EndDate - StartDate) *1440),0) as minst
, round(avg((EndDate - StartDate) *1440),0) as avgst
FROM table1
) t
Yeah, it's fugly, but it's pretty fast. Here's a simpler case, that shows better what's going on:
select dur as "minutes"
, abs(dur) as "unsigned_minutes"
, floor(abs(dur)/60) as "unsigned_whole_hours"
, to_char(floor(abs(dur)/60)) as "hhhh"
, mod(abs(dur),60) as "unsigned_remainder_minutes"
, to_char(mod(abs(dur),60),'FM00') as "mi"
, decode(sign(dur),-1,'-','') as "leading_sign"
, decode(dur,null,'',':') as "colon_separator"
from (select round(( date_expr1 - date_expr2 )*24*60,0) as dur
from ...
)
(replace date_expr1 and date_expr2 with date expressions)
let's unpack this
date_expr1 - date_expr2 returns difference in number of days
multiply by 1440 (24*60) to get duration in minutes
round (or floor) to resolve fractional minutes into integer minutes
divide by 60, integer quotient is hours, remainder is minutes
abs function to get absolute value (change negative values to positive)
to_char format model FM00 give two digits (leading zeros)
use decode function to format a negative sign and a colon (if needed)
The SQL statement could be made less ugly using a PL/SQL function, one that takes two DATE arguments a duration in (fractional) days and returns formatted hhhh:mi
(untested)
create function hhhhmi(an_dur in number)
return varchar2 deterministic
is
begin
if an_dur is null then
return null;
end if;
return decode(sign(an_dur),-1,'-','')
|| to_char(floor(abs(an_dur)*24))
||':'||to_char(mod((abs(an_dur)*1440),60),'FM00');
end;
With the function defined:
SELECT hhhhmi(max(EndDate - StartDate)) as MaximumScheduleTime
, hhhhmi(min(EndDate - StartDate)) as MinimumScheduleTime
, hhhhmi(avg(EndDate - StartDate)) as AverageScheduleTime
FROM table1
You can use the months_between function to convert dates to the difference in years and then use between the decimal years you are interested:
CASE
WHEN ( ( MONTHS_BETWEEN( TO_DATE(date1, 'YYYYMMDD'),
TO_DATE(date1,'YYYYMMDD'))/12
)
BETWEEN Age1DecimalInYears AND Age2DecimalInYears
)
THEN 'It is between the two dates'
ELSE 'It is not between the two dates'
END;
You may need to change date format to match the a given date format and verify that 31 day months work for your specific scenarios.
References:
( found on www on 05/15/2015 )
1. Oracle/PLSQL: MONTHS_BETWEEN Function
2. Oracle Help Center - MONTHS_BETWEEN

Resources