Oracle Week Number From Date ISO Week But From December 1 - oracle

Ok, I've had a query that has been working fine that calculates the week number from December 1 (the start of our Sales Fiscal Year).
Now the requirements have changed. I still need to calculate the week number based on the field (Invoice_Date). However, instead of starting to count from December 1 (Dec 1-7, Week 1, etc.) now I need to start counting on the nearest Monday to December 1st. As I understand it, the ISO week is kind of what I'm looking for but it starts January 1. How do I modify this to work from December 1?
Any help would be greatly appreciated.

select next_day(to_date('0112' || to_char(sysdate, 'YYYY'),'ddmmyyyy') - 1, 'MONDAY') dec_mon from dual; gives you first Monday of December current year
Number of week is just ceil((sysdate - dec_mon)/7).
If you want last Monday before 1st Dec you can get it by:
select next_day(to_date('2511' || to_char(sysdate, 'YYYY'),'ddmmyyyy') - 1, 'MONDAY')
from dual;

In this proposed solution, I build a "helper table" first, showing the Monday_from and Monday_to for each fiscal year (in the third CTE, named ranges). Then I build a few test dates - I was lazy, I should have used to_date() so I can include time-of-day component as well. The join condition in the actual solution (at the end of the code) is written so it works without modification for dates with non-zero "time-of-day" component.
I used the nice feature of Oracle 11.2 which allows us to give column aliases in the declaration of CTEs - otherwise the column aliases would need to be moved inside the respective SELECTs. Otherwise the solution should work at least for Oracle 9 and above (I think).
with
y ( dt ) as (
select add_months(date '2000-12-01', 12 * level )
from dual
connect by level <= 30
),
m ( dt ) as (
select trunc(dt, 'iw') + case when dt - trunc(dt, 'iw') <= 3 then 0 else 7 end
from y
),
ranges ( monday_from, monday_to ) as (
select dt, lead(dt) over (order by dt) - 1
from m
),
test_dates ( t_date ) as (
select date '2013-02-23' from dual union all
select date '2008-12-01' from dual union all
select date '2008-04-28' from dual union all
select date '2016-11-29' from dual
)
select t_date, monday_from, 1 + trunc((t_date - monday_from)/7) as week_no
from test_dates t inner join ranges r
on t.t_date >= r.monday_from and t.t_date < r.monday_to
;
T_DATE MONDAY_FROM WEEK_NO
------------------- ------------------- ----------
2008-04-28 00:00:00 2007-12-03 00:00:00 22
2008-12-01 00:00:00 2008-12-01 00:00:00 1
2013-02-23 00:00:00 2012-12-03 00:00:00 12
2016-11-29 00:00:00 2016-11-28 00:00:00 1

The nearest Monday to any any given date is returned with the following function:
NEXT_DAY(some_date-4,'Monday')
as shown by this query:
with dts(some_date) as (
select date '2006-12-1' from dual
union all
select add_months(some_date,12)
from dts
where some_date <= date '2014-12-1'
)
select some_date
, next_day(some_date-4,'monday') nearest
, some_date - next_day(some_date-4,'monday') dist
from dts;
SOME_DATE NEAREST DIST
----------- ----------- ----------
01-DEC-2006 04-DEC-2006 -3
01-DEC-2007 03-DEC-2007 -2
01-DEC-2008 01-DEC-2008 0
01-DEC-2009 30-NOV-2009 1
01-DEC-2010 29-NOV-2010 2
01-DEC-2011 28-NOV-2011 3
01-DEC-2012 03-DEC-2012 -2
01-DEC-2013 02-DEC-2013 -1
01-DEC-2014 01-DEC-2014 0
01-DEC-2015 30-NOV-2015 1
10 rows selected

Related

How to get last workday before holiday in Oracle [duplicate]

This question already has answers here:
How to get the previous working day from Oracle?
(4 answers)
Closed 1 year ago.
need help for some oracle stuff ..
I need to get Day-1 from sysdate, holiday and weekend will be excluded .
And for holiday, we need to get the range to get the last workday before holiday.
The start date and end date will coming from my holiday table.
ex :
Holiday Table
HolidayName
Start_date
End_Date
holiday1
5th Aug'21
6th Aug'21
condition :
this query run on 9th Aug 2021
expected result :
4th Aug'21
I've tried some query and function but I just can't get what I need.
Thanks a lot for help!
Here's one way to do it.
select max(d) as last_workday
from (select trunc(sysdate)-level as d from dual connect by level < 30) prior_month
where to_char(d, 'DY') not in ('SAT','SUN')
and not exists (select holidayname from holiday_table
where prior_month.d between start_date and end_date)
;
Without seeing your Holiday table, it's hard to say how many days back you would need to look to find the last workday. If you have a holiday that lasts for more than 30 days, you'll need to change the 30 to a larger number.
You can use a simple case expression to determine what day of the week the start of your holiday is, then subtract a number of days based on that.
WITH
holiday (holidayname, start_date, end_date)
AS
(SELECT 'holiday1', DATE '2021-8-5', DATE '2021-8-6' FROM DUAL
UNION ALL
SELECT 'Christmas', DATE '2021-12-25', DATE '2021-12-26' FROM DUAL
UNION ALL
SELECT 'July 4th', DATE '2021-7-4', DATE '2021-7-5' FROM DUAL)
SELECT holidayname,
start_date,
end_date,
start_date - CASE TO_CHAR (start_date, 'Dy') WHEN 'Mon' THEN 3 WHEN 'Sun' THEN 2 ELSE 1 END AS prior_business_day
FROM holiday;
HOLIDAYNAME START_DATE END_DATE PRIOR_BUSINESS_DAY
______________ _____________ ____________ _____________________
holiday1 05-AUG-21 06-AUG-21 04-AUG-21
Christmas 25-DEC-21 26-DEC-21 24-DEC-21
July 4th 04-JUL-21 05-JUL-21 02-JUL-21
You can use a recursive sub-query factoring clause from this answer:
WITH start_date (dt) AS (
SELECT DATE '2021-05-02' FROM DUAL
),
days ( dt, day, found ) AS (
SELECT dt,
TRUNC(dt) - TRUNC(dt, 'IW'),
0
FROM start_date
UNION ALL
SELECT dt - CASE day WHEN 0 THEN 3 WHEN 6 THEN 2 ELSE 1 END,
CASE WHEN day IN (0, 6, 5) THEN 4 ELSE day - 1 END,
CASE WHEN h.start_date IS NULL THEN 1 ELSE 0 END
FROM days d
LEFT OUTER JOIN holidays h
ON ( dt - CASE day WHEN 0 THEN 3 WHEN 6 THEN 2 ELSE 1 END
BETWEEN h.start_date AND h.end_date )
WHERE found = 0
)
SELECT dt
FROM days
WHERE found = 1;
Which, for the sample data:
CREATE TABLE holidays (HolidayName, Start_date, End_Date) AS
SELECT 'holiday1', DATE '2021-08-05', DATE '2021-08-06' FROM DUAL;
Outputs:
DT
2021-08-04 00:00:00
db<>fiddle here
Don't know if it's very efficient. Did it just for fun
create table holidays (
holiday_name varchar2(100) primary key,
start_date date not null,
end_date date not null
)
/
Table created
insert into holidays (holiday_name, start_date, end_date)
values ('holiday1', date '2021-08-05', date '2021-08-06');
1 row inserted
with days_before(day, wrk_day) as
(select trunc(sysdate - 1) d,
case
when h.holiday_name is not null then 0
when to_char(trunc(sysdate - 1), 'D') in ('6', '7') then 0
else 1
end work_day
from dual
left join holidays h
on trunc(sysdate - 1) between h.start_date and h.end_date
union all
select db.day - 1,
case
when h.holiday_name is not null then 0
when to_char(db.day - 1, 'D') in ('6', '7') then 0
else 1
end work_day
from days_before db
left join holidays h
on db.day - 1 between h.start_date and h.end_date
where db.wrk_day = 0) search depth first by day set order_no
select day from days_before where wrk_day = 1;
DAY
-----------
04.08.2021

Just the month january not working

In this query I found the weeks of month, for example november begin 1th Wednesday and ends sunday 5.
These are the first week on november.
SELECT * FROM (
WITH days AS
(SELECT to_date('01012017','ddmmyyyy') + level-1 date_in
FROM dual
CONNECT BY level < 32)
SELECT date_in,
TO_CHAR(date_in,'IW') - TO_CHAR(TRUNC(date_in,'MM'),'IW') + 1 week_number
FROM days) where week_number = 1;
The week_number can change depending of the weeks of the month, but in January is not working.
Your requirement is not clear for me but perhaps you are looking for this:
SELECT TRUNC (next_day(TRUNC(TO_DATE('20022017','ddmmyyyy'), 'mm'),'monday') - 1), 'IW') AS first_week
FROM dual;
I'm still a bit unclear as to what you're looking for, but perhaps the following query will help you on your way:
WITH YEAR_START AS (SELECT TO_DATE('01012017', 'DDMMYYYY') AS FIRST_DAY_OF_YEAR
FROM DUAL),
MONTH_START AS (SELECT FIRST_DAY_OF_YEAR AS FIRST_DAY_OF_MONTH
FROM YEAR_START
UNION ALL
SELECT ADD_MONTHS(FIRST_DAY_OF_YEAR, LEVEL) AS FIRST_DAY_OF_MONTH
FROM YEAR_START
CONNECT BY LEVEL <= 11),
MONTH_START_AND_END AS (SELECT FIRST_DAY_OF_MONTH,
ADD_MONTHS(FIRST_DAY_OF_MONTH, 1) - INTERVAL '1' DAY AS LAST_DAY_OF_MONTH
FROM MONTH_START),
ABS_MONTH_WEEKS AS (SELECT FIRST_DAY_OF_MONTH,
LAST_DAY_OF_MONTH,
TO_NUMBER(TO_CHAR(FIRST_DAY_OF_MONTH, 'IW')) AS ABS_FIRST_WEEK_OF_MONTH,
TO_NUMBER(TO_CHAR(LAST_DAY_OF_MONTH, 'IW')) AS ABS_LAST_WEEK_OF_MONTH
FROM MONTH_START_AND_END),
REL_MONTH_WEEKS AS (SELECT a.*,
1 AS REL_FIRST_WEEK_OF_MONTH,
ABS_LAST_WEEK_OF_MONTH - CASE
WHEN ABS_FIRST_WEEK_OF_MONTH > ABS_LAST_WEEK_OF_MONTH THEN 0
ELSE ABS_FIRST_WEEK_OF_MONTH-1
END AS REL_LAST_WEEK_OF_MONTH
FROM ABS_MONTH_WEEKS a)
SELECT *
FROM REL_MONTH_WEEKS;
Best of luck.

How to convert week number to date range in Oracle?

In Oracle we get week number from following query:
select to_char(TO_DATE(SYSDATE,'DD-MM-YY'),'IW') from dual
I want to get date range of given week number, for example for week no:1 date range is 01-01-2017 to 08-01-2017.
is there any way to get the date range for given week number?
"week no:1 date range is 01-01-2017 to 08-01-2017"
No it isn't. You're confusing 'IW' (which runs MON - SUN) with 'WW' which runs from the first day of the year:
SQL> with dts as (
2 select date '2017-01-01' + (level-1) as dt
3 from dual
4 connect by level <= 8
5 )
6 select dt
7 , to_char(dt, 'DY') as dy_dt
8 , to_char(dt, 'IW') as iw_dt
9 , to_char(dt, 'WW') as ww_dt
10 from dts
11 order by 1;
DT DY_DT IW WW
--------- ------------ -- --
01-JAN-17 SUN 52 01
02-JAN-17 MON 01 01
03-JAN-17 TUE 01 01
04-JAN-17 WED 01 01
05-JAN-17 THU 01 01
06-JAN-17 FRI 01 01
07-JAN-17 SAT 01 01
08-JAN-17 SUN 01 02
8 rows selected.
SQL>
However, it's easy enough to generate a range for the the IW week number. You need to multiple the IW number by 7 which you can convert to a date with the day of year mask. Then you can use next_day() function to get the previous Monday and the next Sunday relative to that date:
SQL> with tgt as (
2 select to_date( &iw *7, 'DDD') as dt from dual
3 )
4 select next_day(dt-8, 'mon') as start_date
5 , next_day(dt, 'sun') as end_date
6* from tgt;
Enter value for iw: 23
old 2: select to_date( &iw *7, 'DDD') as dt from dual
new 2: select to_date( 23 *7, 'DDD') as dt from dual
START_DAT END_DATE
--------- ---------
05-JUN-17 11-JUN-17
SQL>
Obvious this solution uses my NLS Settings (English): you may need to tweak the solution if you use different settings.
These kinds of problems are easy to solve with calendar tables.
The following query builds on the assumption (ISO 8601) that the 4th of January is present in the first week in a year. Therefore I can generate a valid date in the first week of any year by constructing the 4th of January like: to_date(year || '-01-04', 'yyyy-mm-dd'). Oracle will tell me the day of week (sun=1, sat=7) for any date using to_char(date, 'D'). The 4th of JAN 2017 happens to be a wednesday (day 4). Subtracting 3 days will give me the first day (sunday) of the first week of the year.
Now it is easy to find the start day in any given week in the year by simply adding 7 days for each week (not counting the first week).
with weeks as(
select 2017 as year, 39 as week from dual union all
select 2017 as year, 40 as week from dual union all
select 2018 as year, 35 as week from dual
)
select a.*
,to_date(year || '-01-04', 'yyyy-mm-dd') - to_number(to_char(to_date(year || '-01-04', 'yyyy-mm-dd'), 'D')) + 1 + (7 * (week-1)) as start_day
,to_date(year || '-01-04', 'yyyy-mm-dd') + 7 - to_number(to_char(to_date(year || '-01-04', 'yyyy-mm-dd'), 'D')) + (7 * (week-1)) as end_day
from weeks a;
Edit: These are the "convert" expressions you need to convert from week to date range. Note that 2017 and 39 are variable...
start date = to_date(2017 || '-01-04', 'yyyy-mm-dd') - to_number(to_char(to_date(2017 || '-01-04', 'yyyy-mm-dd'), 'D')) + 1 + (7 * (39-1))
end date = to_date(2017 || '-01-04', 'yyyy-mm-dd') + 7 - to_number(to_char(to_date(2017 || '-01-04', 'yyyy-mm-dd'), 'D')) + (7 * (39-1))
Here's a query to list all ISO weeks from 2001 to 2099
SELECT TO_CHAR(TRUNC(dt, 'IW') + 6, 'IYYY-IW') AS week,
TRUNC(dt, 'IW') AS start_date,
TRUNC(dt, 'IW') + 6 AS end_date
FROM (SELECT DATE '2001-01-01' + ((LEVEL - 1) * 7) dt
FROM DUAL
CONNECT BY LEVEL <= 5165);
For the first and last week of year this query needs some CASE logic, but for other weeks works good. This solution use current NLS settings.
select to_char( start_of_week, 'day dd.mm.yyyy' ) start_of_week,
to_char( start_of_week + 6, 'day dd.mm.yyyy' ) end_of_week
from
(
select trunc( date '2017-01-01' + 38*7 , 'day') start_of_week
from dual
)
1) date '2017-01-01' - in what year we look for weeks
or it may be trunc (sysdate, 'YEAR') to take first day of current year
2) date '2017-01-01' + 38*7 - jump to 38th week
3) trunc ( ... , 'day' ) - gives date of first day of the week
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions201.htm
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions230.htm
I use this function:
FUNCTION ISOWeekDate(WEEK INTEGER, YEAR INTEGER) RETURN DATE DETERMINISTIC IS
res DATE;
BEGIN
IF WEEK > 53 OR WEEK < 1 THEN
RAISE VALUE_ERROR;
END IF;
res := NEXT_DAY(TO_DATE( YEAR || '0104', 'YYYYMMDD' ) - 7, 'MONDAY') + ( WEEK - 1 ) * 7;
IF TO_CHAR(res, 'fmIYYY') = YEAR THEN
RETURN res;
ELSE
RAISE VALUE_ERROR;
END IF;
END ISOWeekDate;
Please note, according to my comment it is ambiguous if you only provide a week number without a year. The function returns the first day of given ISO Week.

Calculate the number of days per month between two dates

Using Oracle 12c, I need to run a script on an existing summary table of projects. The summary table has a project, a start date, and an end date. I need to break this data out into the number of days per month for each project.
An example is Project A has a start date of 2/10/2016 and an end date of 3/10/2016. My ending result for this example should be:
Project A, February, 19
Project A, March, 10
This was an easier one as some dates may span 2 or 3 months. This doesn't seem too difficult but for some reason I'm having trouble wrapping my head around it and overthinking it. Does someone have an quick and easy solution to this? I would like to run this as a SQL statement but a PL/SQL script would also work.
In this solution we don't assume any prior knowledge of the time period covered. Also, this solution does not use joins (which may be important for performance).
with
-- begin test data (this section can be deleted)
inputs ( project, start_date, end_date ) as (
select 'A', date '2014-10-03', date '2014-12-15' from dual union all
select 'B', date '2015-03-01', date '2015-03-31' from dual union all
select 'C', date '2015-11-30', date '2016-03-01' from dual
),
-- end test data; solution begins here (it includes the word "with" from the first line)
prep ( project, end_date, dt ) as (
select project, end_date, start_date from inputs union all
select project, end_date, end_date + 1 from inputs union all
select project, end_date, add_months( trunc(start_date, 'mm'), level )
from inputs
connect by add_months (trunc(start_date, 'mm'), level) <= end_date
and prior project = project
and prior sys_guid() is not null
),
computations ( project, dt, month, day_count ) as (
select project, dt, to_char(dt, 'Mon-yyyy'),
lead(dt) over (partition by project order by dt) - dt
from prep
where dt <= end_date + 1
)
select project, month, day_count
from computations
where day_count > 0
order by project, dt
;
OUTPUT:
PROJECT MONTH DAY_COUNT
------- -------- ---------
A Oct-2014 29
A Nov-2014 30
A Dec-2014 15
B Mar-2015 31
C Nov-2015 1
C Dec-2015 31
C Jan-2016 31
C Feb-2016 29
C Mar-2016 1
9 rows selected
If you can do an assumption on the maximum number of days for a project (1000 in my example), you can use the following:
with yourTable(project, startDate, endDate) as
(
select 'Project a' as project,
date '2016-02-10' as startDate,
date '2016-03-10' as endDate
from dual
UNION ALL
select 'Project XX',
date '2016-01-01',
date '2016-01-10'
from dual
)
select project, to_char(startDate + n, 'MONTH'), count(1)
from yourTable
inner join (
select level n
from dual
connect by level <= 1000
)
on (startDate + n <= endDate)
group by project, to_char(startDate + n, 'MONTH')
The part with the CONNECT BY is used as a date generator, assuming that every project is at maximum 1000 days long; the external query uses the date generator to split the row of a project in many rows, one for each day between start and end date, and then aggregates by month and project to build the output.
A slightly different way, based on months and not days, could be:
select project, to_char(add_months(startDate, n ), 'MONTH'),
case
when trunc(add_months(startDate, n ), 'MONTH') = trunc(add_months(endDate, n ), 'MONTH')
then endDate - startDate +1
when trunc(add_months(startDate, n ), 'MONTH') <= startDate
then last_day(add_months(startDate, n)) - startDate
when last_day(add_months(startDate, n )) >= endDate
then endDate - trunc(add_months(startDate, n ), 'MONTH') +1
else
last_day(add_months(startDate, n )) - trunc(last_day(add_months(startDate, n )), 'MONTH')
end as numOfDays
from yourTable
inner join (
select level -1 n
from dual
connect by level <= 1000
)
on trunc(add_months(startDate, n ), 'MONTH') <= trunc(endDate, 'MONTH')
This is a bit more complicated, to handle the different cases, but more efficient, given that it works at month level, not day level
I think you're after something like:
WITH sample_data AS (SELECT 'A' PROJECT, to_date('10/02/2016', 'dd/mm/yyyy') start_date, to_date('10/03/2016', 'dd/mm/yyyy') end_date FROM dual UNION ALL
SELECT 'B' PROJECT, to_date('10/02/2016', 'dd/mm/yyyy') start_date, to_date('10/06/2016', 'dd/mm/yyyy') end_date FROM dual UNION ALL
SELECT 'C' PROJECT, to_date('10/02/2016', 'dd/mm/yyyy') start_date, to_date('18/02/2016', 'dd/mm/yyyy') end_date FROM dual)
SELECT PROJECT,
to_char(add_months(trunc(start_date, 'mm'), LEVEL -1), 'fmMonth yyyy', 'nls_date_language=english') mnth,
CASE WHEN trunc(end_date, 'mm') = add_months(trunc(start_date, 'mm'), LEVEL -1)
THEN end_date
ELSE add_months(trunc(start_date, 'mm'), LEVEL) -1
END - CASE WHEN trunc(start_date, 'mm') = add_months(trunc(start_date, 'mm'), LEVEL -1)
THEN start_date + 1
ELSE add_months(trunc(start_date, 'mm'), LEVEL -1)
END + 1 num_days
FROM sample_data
CONNECT BY PRIOR PROJECT = PROJECT
AND PRIOR sys_guid() IS NOT NULL
AND add_months(trunc(start_date, 'mm'), LEVEL -1) <= TRUNC(end_date, 'mm');
PROJECT MNTH NUM_DAYS
------- -------------- ----------
A February 2016 19
A March 2016 10
B February 2016 19
B March 2016 31
B April 2016 30
B May 2016 31
B June 2016 10
C February 2016 8
This uses the multi-row connect-by-level technique (the presence of the and prior sys_guid() is not null enables the connect by to loop through each row separately) to loop through each project row in the sample_data table (you presumably have the project information in a table already, so you wouldn't need to have the sample_data subquery at all; you could just reference your table directly in the main SQL).
We then compare the month of the start date with the month of the row being generated by the connect by, and if it's the same month, then we know we need to use the start date, otherwise we use the first of the month of the generated row; we do similarly for the end date.
That way, we can now subtract one from the other and make adjustments to make the calculation correct. You may need to tweak this yourself if you need a start and end date of the same day to count as 1 day, rather than 0 - it'll probably need an extra case statement to take account of when the start and end date are in the same month.
Using this approach won't limit your project length; it could be as long as you liked.
ETA: Looks like Mathguy posted an answer whilst I was typing out my answer, and whilst our basic methods are the same, mine doesn't use an analytic function to determine the difference in the number of days. You may or may not find their answer more performant than mine - you should test both to see which one works best with your data.

Retrieve rows with 0 count from Oracle

I am woking on a query which can give back the count divided by month about the offices that will be closed this summer.
SELECT
qa.tmonth,
COUNT(qa.tmonth) AS qtn
FROM
(
SELECT TO_CHAR(CLOSURE_DATE, 'yyyymm') AS tmonth
FROM Holidays
WHERE CLOSURE_DATE >= TO_DATE('20160501', 'YYYY-MM-DD') AND
CLOSURE_DATE <= TO_DATE('20160901', 'YYYY-MM-DD')
) qa
GROUP BY qa.tmonth;
Since the months: May, June, August and September no office will be closed the output is the following:
TMONTH|QTN
201607|80
But I need a thing like this
TMONTH|QTN
201605|0
201606|0
201607|80
201608|0
201609|0
How could I achieve that?
Thanks to all!
You can try with something like this:
SQL> with holidays(closure_date) as
2 (
3 select date '2016-07-01' from dual union all
4 select date '2016-07-02' from dual union all
5 select date '2016-07-03' from dual union all
6 select date '2016-07-04' from dual union all
7 select date '2016-07-05' from dual
8 )
9 select count(closure_date) as closure_days, to_char(day, 'yyyymm') as month
10 from (
11 select date '2016-05-01' + level -1 as day
12 from dual
13 connect by date '2016-05-01' + level -1 <= date '2016-09-30'
14 ) days
15 left outer join holidays
16 on (day = closure_date)
17 group by to_char(day, 'yyyymm') ;
CLOSURE_DAYS MONTH
------------ ------
0 201608
5 201607
0 201606
0 201605
0 201609
SQL>
This uses a query to build the list of all the days between a starting and an ending date; I used 01/05 and 30/09 and called it days.
Then it queries days with the holidays table in outer join; this way you can count only the days for which there is a corrensponding value in the closure days list, thus counting the closure days for each day, month year; the aggregation for year and month completes the job
A similar approach like above. Tip: You can execute the two sub-queries separately, to analyse the logic.
select to_char (m.month, 'yyyymm') as TMONTH, m.month,
nvl (h.qtn, 0) as QTN
from
(
SELECT add_months(trunc (SYSDATE, 'MONTH'), -(LEVEL-1)) as MONTH
FROM dual
CONNECT BY LEVEL <= 12 -- generate a list of the last 12 month
) m
left join
(
SELECT trunc (closure_date, 'MONTH') as MONTH,
count (*) as QTN
FROM Holidays
group by trunc (closure_date, 'MONTH')
) h
on m.MONTH = h.MONTH
where m.month between DATE '2016-01-01' and sysdate
order by TMONTH desc;

Resources