SQL Showing Rolling 4 Months, Looking for Fiscal Year - sql

I have been provided the below SQL query and am looking to modify it. Currently this is only pulling the last 4 months worth of data. I am trying to modify it to run from December 1 through the current month. If anyone is able to guide me through this, that would be greatly appreciated.
Select NVL(x.COUNT, 0) + z.COUNT As "Number of RMAs",
Case z.MONTH When 1 Then 'Jan.' When 2 Then 'Feb.' When 3 Then 'Mar.'
When 4 Then 'Apr.' When 5 Then 'May' When 6 Then 'Jun.' When 7 Then 'Jul.'
When 8 Then 'Aug.' When 9 Then 'Sep.' When 10 Then 'Oct.'
When 11 Then 'Nov.' When 12 Then 'Dec.' Else 'error' End As "Month",
z.YEAR,
'28.00' As GOAL,
Round(Avg(NVL(x.COUNT, 0) + z.COUNT) Over (Order By z.YEAR, z.MONTH Rows
Between 2 Preceding And Current Row), 2) As ROLL3MOAVG
From (Select Count(RMA.ID) As count,
Extract(Month From RMA.RMA_DATE) As month,
Extract(Year From RMA.RMA_DATE) As year
From RMA,
(Select Unique RMA_DETAIL.RMA_ID
From RMA_DETAIL
Where RMA_DETAIL.RETURN_CODE_ID > 0 And RMA_DETAIL.RETURN_CODE_ID <
100) RMA_DETAIL
Where RMA.ID = RMA_DETAIL.RMA_ID And RMA.RMA_DATE >= SysDate - 400
Group By Extract(Month From RMA.RMA_DATE),
Extract(Year From RMA.RMA_DATE)) x,
(Select 0 As count,
Extract(Month From Add_Months(SysDate - 120, (Level - 1))) As month,
Extract(Year From Add_Months(SysDate - 120, (Level - 1))) As year
From dual
Connect By Level <= 13) z
Where z.MONTH = x.MONTH(+) And z.YEAR = x.YEAR(+)
Order By z.YEAR,
z.MONTH

Don't have access to any sample data, so I would try the following: The correlated subquery aliased as "z" calculates your date boundaries:
SELECT 0 AS count,
EXTRACT(MONTH FROM Add_Months(SysDate - 120, (LEVEL - 1))) AS month,
EXTRACT(YEAR FROM Add_Months(SysDate - 120, (LEVEL - 1))) AS year
FROM dual
CONNECT BY LEVEL <= 13
COUNT MONTH YEAR
---------- ---------- ----------
0 2 2020
0 3 2020
0 4 2020
0 5 2020
0 6 2020
0 7 2020
0 8 2020
0 9 2020
0 10 2020
0 11 2020
0 12 2020
0 1 2021
0 2 2021
It starts with the month 120 days ago (SysDate - 120) and adds 12 month rows. Run this query in sqldeveloper to check what it does. You'll see it starts in feb 2020 and ends in feb 2021.
If you replace that query with a query that starts on dec 1st up till now it should work:
SELECT 0 AS count,
EXTRACT(MONTH FROM ADD_MONTHS(TO_DATE('01-DEC-2019','DD-MON-YYYY'), (LEVEL - 1))) As month,
EXTRACT(YEAR FROM ADD_MONTHS(TO_DATE('01-DEC-2019','DD-MON-YYYY'), (LEVEL - 1))) As year
FROM dual
CONNECT BY LEVEL <= ROUND(MONTHS_BETWEEN(SYSDATE,TO_DATE('01-DEC-2019','DD-MON-YYYY'))) + 1
COUNT MONTH YEAR
---------- ---------- ----------
0 12 2019
0 1 2020
0 2 2020
0 3 2020
0 4 2020
0 5 2020
0 6 2020
explanation:
start with ADD_MONTHS(TO_DATE('01-DEC-2019','DD-MON-YYYY'), (LEVEL - 1)). Your first row will be LEVEL 1 so this will return December.
CONNECT BY LEVEL <= ROUND(MONTHS_BETWEEN(SYSDATE,TO_DATE('01-DEC-2019','DD-MON-YYYY'))) + 1: add a month up til current month so calculate the number of months between now and dec 1st and add 1.
For getting a query that works for every year (there must be cleaner ways to write this but I can't think of any now), use the following:
SELECT 0 AS count,
EXTRACT(MONTH FROM ADD_MONTHS(TO_DATE('01-DEC-'||TO_CHAR(ADD_MONTHS(SYSDATE,-11),'YYYY'),'DD-MON-YYYY'), (LEVEL - 1))) As month,
EXTRACT(YEAR FROM ADD_MONTHS(TO_DATE('01-DEC-'||TO_CHAR(ADD_MONTHS(SYSDATE,-11),'YYYY'),'DD-MON-YYYY'), (LEVEL - 1))) As year
FROM dual
CONNECT BY LEVEL <= ROUND(MONTHS_BETWEEN(SYSDATE,TO_DATE('01-DEC-'||TO_CHAR(ADD_MONTHS(SYSDATE,-11),'YYYY'),'DD-MON-YYYY'))) + 1

Related

Oracle - Get all days from month

How create select which return all days from month where month = PARAMETER_MONTH eg. 5 and year = extract(year from sysdate).
1
2
3
..
30
31
"Row generator" is the search keyword. For example:
SQL> with temp (col) as
2 (select to_date(&par_month, 'mm') from dual)
3 select to_number(to_char(col + level - 1, 'dd')) day
4 from temp
5 connect by level <= last_day(col) - col + 1
6 order by day;
Enter value for par_month: 5
DAY
----------
1
2
3
4
5
6
<snip>
29
30
31
31 rows selected.
SQL>
TO_DATE function will convert entered value into the 1st day of that month in current year, so you don't have to worry about "and year = extract(year from sysdate)" you mentioned in question.

Calculate manual week number of year in Oracle SQL

i have a date column and along that i need to calculate another column in oracle for week number of they year, the weeks should be from sunday to saturday, starting first day of the year.
for example for current year
Week 1 : 1 Jan 2020 (Wednesday) - 4 Jan 2020(Saturday)
Week 2 : 5 Jan 2020 (Sunday) - 11 Jan 2020(Saturday)
. . . . .
Week 5 : 26 Jan 2020 (Sunday) - 1 Feb 2020 (Saturday)
and so on...
You need to write your own logic using a hierarchy query.
Something like the following:
SQL> SELECT WEEKNUMBER,
2 WEEK_START,
3 CASE WHEN WEEKNUMBER = 1 THEN FIRST_WEEKEND ELSE WEEK_START + 6 END AS WEEK_END
4 FROM
5 (SELECT
6 CASE
7 WHEN LEVEL = 1 THEN FIRST_DAY
8 ELSE FIRST_WEEKEND + ( LEVEL - 2 ) * 7 + 1
9 END AS WEEK_START,
10 FIRST_WEEKEND,
11 LEVEL AS WEEKNUMBER
12 FROM
13 ( SELECT
14 TRUNC(SYSDATE, 'YEAR') FIRST_DAY,
15 NEXT_DAY(TRUNC(SYSDATE, 'YEAR'), 'SATURDAY') FIRST_WEEKEND
16 FROM DUAL )
17 CONNECT BY
18 CASE WHEN LEVEL = 1 THEN FIRST_DAY
19 ELSE FIRST_WEEKEND + ( LEVEL - 2 ) * 7 + 1
20 END < ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 12));
WEEKNUMBER WEEK_STAR WEEK_END
---------- --------- ---------
1 01-JAN-20 04-JAN-20
2 05-JAN-20 11-JAN-20
3 12-JAN-20 18-JAN-20
4 19-JAN-20 25-JAN-20
5 26-JAN-20 01-FEB-20
6 02-FEB-20 08-FEB-20
7 09-FEB-20 15-FEB-20
8 16-FEB-20 22-FEB-20
9 23-FEB-20 29-FEB-20
10 01-MAR-20 07-MAR-20
11 08-MAR-20 14-MAR-20
.......
.......
53 27-DEC-20 02-JAN-21
Cheers!!
One other option would be
with t as
(
select trunc(sysdate,'yyyy')+level-1 as day, to_char(trunc(sysdate,'yyyy')+level-1,'DY','NLS_DATE_LANGUAGE=AMERICAN') as weekday, level - 1 as lvl
from dual
connect by level <= 366
), t1 as
(
select case
when lvl = 0 then
day
else
case
when weekday = 'SUN' then
day
end
end as day1,
lvl
from t
), t2 as
(
select case
when weekday = 'SAT' then
day
end as day2,
lvl
from t
)
select concat( 'Week ', wk1) as week, day1, day2
from
(
select row_number() over (order by lvl) wk1, day1
from t1 where day1 is not null ) t1
left join
(
select row_number() over (order by lvl) wk2, day2
from t2 where day2 is not null ) t2
on wk2 = wk1
by using select .. from dual connect by level syntax and case..when expression to scan all the current year.
Demo

Counting the number of days excluding sunday between two dates

I am trying to calculate number of days betwen two dates excluding sundays. This is my query,
SELECT F_PLANHM_END_DT
- F_PLANHM_ST_DT
- 2
* (TO_CHAR (F_PLANHM_END_DT, 'WW') - TO_CHAR (F_PLANHM_ST_DT, 'WW'))
FROM VW_S_CURV_PROC
WHERE HEAD_MARK = 'IGG-BLH-BM 221';
SELECT COUNT (*)
FROM (SELECT SYSDATE + l trans_date
FROM ( SELECT LEVEL - 1 l
FROM VW_S_CURV_PROC
CONNECT BY LEVEL <= ( (SYSDATE + 7) - SYSDATE)))
WHERE TO_CHAR (trans_date, 'dy') NOT IN ('sun');
I am retrieving date from a view called VW_S_CURV_PROC with start date : F_PLANHM_ST_DT and end date F_PLANHM_END_DT. Somehow i cant make this to work. Please help me...
You could use the ROW GENERATOR technique to first generate the dates for a given range, and then exclude the SUNDAYs.
For example, this query will give me the total count of days between 1st Jan 2014 and 31st Dec 2014, excluding the Sundays -
SQL> WITH DATA AS
2 (SELECT to_date('01/01/2014', 'DD/MM/YYYY') date1,
3 to_date('31/12/2014', 'DD/MM/YYYY') date2
4 FROM dual
5 )
6 SELECT SUM(holiday) holiday_count
7 FROM
8 (SELECT
9 CASE
10 WHEN TO_CHAR(date1+LEVEL-1, 'DY','NLS_DATE_LANGUAGE=AMERICAN') <> 'SUN'
11 THEN 1
12 ELSE 0
13 END holiday
14 FROM data
15 CONNECT BY LEVEL <= date2-date1+1
16 )
17 /
HOLIDAY_COUNT
-------------
313
SQL>

Getting Month values for specific year

Fisc_prd stands for month
fisc_yr stands for year
I need the result in such a way that if current month is 01(that is Jan) then i need all the period for previous year i.e 1 to 12 and if the current month is not equal to 01 then i need all the period for current year less than current month(e.g if the current month is 3 then i need 1 to 2 as fisc_prd)
I can only get all the fisc_prd for previous year but cant get fisc_prd less than current month for current year. In Below query, CALENDAR table contains all the Month and Year values.By usig this query i can get all the fisc_prd for previous year when current month is 01.
SELECT FISC_PRD,FISC_YR FROM CALENDAR WHERE FISC_YR=(SELECT DECODE(TO_NUMBER(TO_CHAR(SYSDATE,'MM')),01,(TO_NUMBER(TO_CHAR(SYSDATE,'YYYY'))-1) ,TO_NUMBER(TO_CHAR(SYSDATE,'YYYY'))) FROM DUAL)
Please share your ideas
If you're basing this on the current date then you can use a connect-by hierarchical query to get the period and year:
select extract(month from add_months(sysdate, 1-level)) as fisc_prd,
extract(year from add_months(sysdate, 1-level)) as fisc_year
from dual
where level > 1
connect by level <= case when extract(month from sysdate) = 1 then 13
else extract(month from sysdate) end;
Today that gives:
FISC_PRD FISC_YEAR
---------- ----------
1 2015
To check how it behaves for other dates you can use a modified version that uses a specific date but the same logic; I'm using a bind variable here:
variable dt varchar2;
exec :dt := '2015-01-17';
select extract(month from add_months(to_date(:dt, 'YYYY-MM-DD'), 1-level)) as fisc_prd,
extract(year from add_months(to_date(:dt, 'YYYY-MM-DD'), 1-level)) as fisc_year
from dual
where level > 1
connect by level <= case
when extract(month from to_date(:dt, 'YYYY-MM-DD')) = 1 then 13
else extract(month from to_date(:dt, 'YYYY-MM-DD'))
end;
FISC_PRD FISC_YEAR
12 2014
11 2014
10 2014
9 2014
8 2014
7 2014
6 2014
5 2014
4 2014
3 2014
2 2014
1 2014
Changing the value of dt seems to give the result you described; for example with
exec :dt := '2015-03-17';
you get:
FISC_PRD FISC_YEAR
---------- ----------
2 2015
1 2015
But this is just to test the logic, you can just use sysdate rather than a bind variable.
The 1-level and using 13 if the current month is January are because you don't want the current month to be included in the result.
This ought to work:
with sample_data as (select to_number(to_char(add_months(sysdate, 6 - level), 'mm')) fisc_prd,
to_number(to_char(add_months(sysdate, 6 - level), 'yyyy')) fisc_yr
from dual
connect by level <= 24)
select fisc_prd,
fisc_yr
from sample_data
where case when to_char(sysdate, 'mm') = '01' then to_number(to_char(sysdate, 'yyyy')) -1
else to_number(to_char(sysdate, 'yyyy')) end = fisc_yr
and case when to_char(sysdate, 'mm') = '01' then 13
else to_number(to_char(sysdate, 'mm')) end > fisc_prd;
If your colum "FISC_YR" is datetime datatype then the below query will works for you
SELECT FISC_PRD,FISC_YR
FROM CALENDAR
WHERE
(month(getdate())!=1 AND month(FISC_YR) in(month(getdate())- 1,month(getdate())-2) AND year(getdate())=year(FISC_YR))
OR
(month(getdate())=1 AND year(getdate())-1=year(FISC_YR))

Classifying months in periods

Suppose I have 2 years of data. From January 2010 to Dec 2011.
I want to classify each of the months as periods. So January 2010 will be my 1, February 2010 my 2, and so on until December 2011 my 24 period.
I know I could do it something like:
select
year,mn,
case when year=2010 and mn=01 then 1
else when year=2010 and mn=02 then 2
else when year=2010 and mn=03 then 3
//and so on until // else when year=2011 and mn=12 then 24 end
from mytable;
The result would be something like:
year mn period
2010 1 1
2010 2 2
2010 3 3
2010 4 4
2010 5 5
2010 6 6
2010 7 7
2010 8 8
2010 9 9
2010 10 10
2010 11 11
2010 12 12
2011 1 13
2011 2 14
2011 3 15
2011 4 16
2011 5 17
2011 6 18
2011 7 19
2011 8 20
2011 9 21
2011 10 22
2011 11 23
2011 12 24
I want to avoid this kind of long and not wise method.
select
year, mn,
row_number() over (order by year, mn) as period
from t
No need for fancy windowing functions. Just do it the simple way. For a given {epoch-year} and {epoch-month} (e.g., 2010 and 1 respectively), the formula
( ( 12*year + mn ) - ( 12*{epoch-year} + {epoch-month} )
will give you the offset in month from the epoch. Add 1 to that and you have your period number. That leads you to something like this:
select year ,
mn ,
( ( 12*year + mn )
- ( 12*{epoch-year} + {epoch-month} )
) + 1 as period
...
from some-table
where year > {epoch-year}
OR ( year = {epoch-year} and mn >= {epoch-month} )
IF you don't have a specific epoch in mind, you can do something like this:
select t.year ,
t.mn ,
( ( 12*year + mn )
- ( 12*epoch.year + epoch.month )
) + 1 as period
...
from ( select year,mn
from some-table
order by year , mn
limit 1
) epoch
cross join some-table t
You should note that one can come up with a formula to number periods based on period lengths longer than 1 month: just compute the offset in months and and use integer division to divide that offset by the period length in months, thus getting to the sequential period number, something like
( ( 12*year + mn )
- ( 12*2010 + 1 )
) DIV 3 + 1 as period
should give you periods of 3 months in length
A cheap version for this particular case:
SELECT year, mn, (year - 2010) * 12 + mn AS period
FROM tbl;
This would also account for months that may be missing in your data.
And it would give you consistent numbers even when only selecting some rows.