Generate 3 years - sql

Tying to generate 3 years of data as below
31-DEC-17
31-DEC-18
31-DEC-19
below query generates 3 months. How do i transfer this to 3 years
select LAST_DAY(add_months(date '2017-01-01', level - 1)) as mth
from dual
connect by level <= 3

Multiply parameter by 12:
select last_day(add_months(date '2017-01-01', (level - 1) * 12 )) as mth
from dual
connect by level <= 3
demo

use <=36 which will generate till 31-DEC-19
select LAST_DAY(add_months(date '2017-01-01', level - 1)) as mth
from dual
connect by level <= 36

As #APC mentions, last_day function depends on your NLS Date Settings. So, you may prefer truncate function truncated to the unit specified by yyyy as below instead :
select add_months(trunc(date'2017-01-01','yyyy'), level * 12 ) - 1
as "Last Days"
from dual
connect by level <= 3;
Last Days
----------
2017-12-31
2018-12-31
2019-12-31
Demo

Related

Oracle SQL: loop through year

I need to run this query to get 5 years data (2015 to 2019), and I am wondering if there is a way to automatically loop through year, instead of manually changing the year (e.g., from 2015 to 2016) and running this query 5 times? Any help will be appreciated! Thanks!
Select ID,program, open_date, close_date
From clients
Where open_date=to_date('01/01/2015','mm/dd/yyyy')
and close_date=to_date('12/31/2015','mm/dd/yyyy')
You can always generate calendar you need and join it with your query.
with cal as (
select add_months(date '2015-01-01', (level - 1)*12) as start_dt,
add_months(date '2015-12-31', (level - 1)*12) as end_dt
from dual
connect by level <= 5
)
Select c.ID, c.program, c.open_date, c.close_date
From clients c
join cal
on c.open_date=cal.start_dt and c.close_date=cal.end_dt
Row generator it is. For example:
SQL> with period (start_year, end_year) as
2 (select 2015, 2020 from dual)
3 select d.dummy, p.start_year + level - 1 as year
4 from dual d cross join period p
5 connect by level <= end_year - start_year
6 order by year;
D YEAR
- ----------
X 2015
X 2016
X 2017
X 2018
X 2019
SQL>
Applied to your code (can't test it, don't have your tables):
with
period (start_year, end_year) as
(select 2015, 2020 from dual),
select c.id, c.program, c.open_date, c.close_date
from clients c cross join period p
where open_date = add_months(trunc(to_date(p.start_year, 'yyyy'), 'yyyy'), 12 * (level - 1))
and close_date = add_months(trunc(to_date(p.start_year, 'yyyy'), 'yyyy'), 12 * (level )) - 1
connect by level <= p.end_year - p.start_year;
because those values produce
SQL> with
2 period (start_year, end_year) as
3 (select 2015, 2020 from dual)
4 select add_months(trunc(to_date(p.start_year, 'yyyy'), 'yyyy'), 12 * (level - 1)) a,
5 add_months(trunc(to_date(p.start_year, 'yyyy'), 'yyyy'), 12 * (level )) - 1 b
6 from period p
7 connect by level <= end_year - start_year;
A B
---------- ----------
01.01.2015 31.12.2015
01.01.2016 31.12.2016
01.01.2017 31.12.2017
01.01.2018 31.12.2018
01.01.2019 31.12.2019
SQL>
Take a look at the following code snippet:
SELECT EXTRACT(YEAR FROM TO_DATE('01.01.2015', 'dd.mm.yyyy')) + ROWNUM - 1 AS "YEAR"
FROM dual
CONNECT BY ROWNUM <= 5

How to find out which months in a year range that's having 5 Saturdays between two given dates as per the inputs?

Only through Query mode, no execution block method.
i have tried the following but no luck
SELECT
TO_CHAR(dt, 'mon'),
COUNT(*) AS noofsat
FROM
(
SELECT
from_date + 1 AS dt
FROM
dual
CONNECT BY
level <= (
SELECT
TO_DATE - from_date
FROM
dual
)
)
WHERE
TO_CHAR(dt, 'd') = 7
GROUP BY
TO_CHAR(dt, 'mon')
HAVING
COUNT(*) = 5
i need only the month and year which has five Saturdays like August 2019
Change your date source to:
SELECT
TO_DATE('20190101','YYYYMMDD') + (LEVEL - 1) AS dt
FROM
dual
CONNECT BY
level <= 365
This is a very good question, In my situation I needed exactly the same thing but in SQL Server, and this is how I did it, I will post the answer for those that end up here looking for a SQL Server solution.
I ended up building a Date Dimension in my Data Warehouse and querying it:
DROP TABLE IF EXISTS ##Dates
CREATE TABLE ##Dates(DateValue Date,
YearValue INT,
MonthValue INT,
DayValue INT,
WeekDayValue INT)
DECLARE #start DATE = '2019-01-01'
WHILE #start < '2019-12-31'
BEGIN
INSERT INTO ##Dates(DateValue,
YearValue,
MonthValue,
DayValue,
WeekDayValue)
VALUES(#start,
DATEPART(YY,#start),
DATEPART(mm,#start), -- In oracle TO_CHAR(dt, 'mon')
DATEPART(dd,#start),
DATEPART(dw,#start)) --In oracle TO_CHAR(dt, 'd')
SET #start = DATEADD(dd,1,#start)
END
SELECT MonthValue, SUM(CASE WHEN WeekDayValue = 7 THEN 1 ELSE 0 END) AS NumOfSaturdays FROM ##Dates
WHERE DateValue BETWEEN '2019-01-01' AND '2019-12-01'
GROUP BY MonthValue
HAVING SUM(CASE WHEN WeekDayValue = 7 THEN 1 ELSE 0 END) > 4
Output:
MonthValue NumOfSaturdays
3 5
6 5
8 5
11 5
You could get the start of each month covered by your date range; this uses a pair of made-up fixed values as it isn't clear where you are getting your range from:
select add_months(trunc(date '2019-03-15', 'MM'), level - 1) as month_start
from dual
connect by level <= ceil(months_between(date '2019-11-21', date '2019-03-15'));
MONTH_STAR
----------
2019-03-01
2019-04-01
2019-05-01
2019-06-01
2019-07-01
2019-08-01
2019-09-01
2019-10-01
2019-11-01
9 rows selected.
You can then manipulate those dates in various ways. You can use the next_day() function to find the first Saturday of the month - based on the previous day, in case the 1st of the month is itself a Saturday. If you then add 28 days to that, you get the fifth Saturday of that month - or, for months with only four Saturdays, the first Saturday in the following month. So if you compare those generated Saturdays, if they are in the same month then that month must actually have five Saturdays.
select month_start,
next_day(month_start - 1, 'SATURDAY') as first_sat,
next_day(month_start - 1, 'SATURDAY') + 28 as poss_fifth_sat,
extract(month from month_start) as month_num,
extract(month from (next_day(month_start - 1, 'SATURDAY') + 28)) as poss_fifth_sat_month
from (
select add_months(trunc(date '2019-03-15', 'MM'), level - 1) as month_start
from dual
connect by level <= ceil(months_between(date '2019-11-21', date '2019-03-15'))
);
MONTH_STAR FIRST_SAT POSS_FIFTH MONTH_NUM POSS_FIFTH_SAT_MONTH
---------- ---------- ---------- ---------- --------------------
2019-03-01 2019-03-02 2019-03-30 3 3 -- same
2019-04-01 2019-04-06 2019-05-04 4 5
2019-05-01 2019-05-04 2019-06-01 5 6
2019-06-01 2019-06-01 2019-06-29 6 6 -- same
2019-07-01 2019-07-06 2019-08-03 7 8
2019-08-01 2019-08-03 2019-08-31 8 8 -- same
2019-09-01 2019-09-07 2019-10-05 9 10
2019-10-01 2019-10-05 2019-11-02 10 11
2019-11-01 2019-11-02 2019-11-30 11 11 -- same
9 rows selected.
You don't actually need to see those values, I'm just showing the working. But you can use the last two as a filter instead:
select month_start
from (
select add_months(trunc(date '2019-03-15', 'MM'), level - 1) as month_start
from dual
connect by level <= ceil(months_between(date '2019-11-21', date '2019-03-15'))
)
where extract(month from month_start)
= extract(month from (next_day(month_start - 1, 'SATURDAY') + 28));
MONTH_STAR
----------
2019-03-01
2019-06-01
2019-08-01
2019-11-01
You can then format those dates however you wish.
One possible issue (I'm sure there are others) is mentioned in the documentation:
The argument char must be a day of the week in the date language of your session, either the full name or the abbreviation.
So the code above will work as long as your session date language is English. If you can be 100% sure that everyone running this will always be using the same session settings then you use that fixed value (or whatever language is appropriate).
If you can't control that then you can't override the date language as you can with some other functions. As a workaround you could use a known Saturday date to obtain the current date language's name or abbreviation for Saturday, without you needing to know what the language actually is:
alter session set nls_date_language = 'Spanish';
select month_start
from (
select add_months(trunc(date '2019-03-15', 'MM'), level - 1) as month_start
from dual
connect by level <= ceil(months_between(date '2019-11-21', date '2019-03-15'))
)
where extract(month from month_start)
= extract(month from (next_day(month_start - 1, to_char(date '2000-01-01', 'Dy')) + 28));
MONTH_STAR
----------
2019-03-01
2019-06-01
2019-08-01
2019-11-01

Problem with getting the quarter from a date in Oracle

I've written a query to get the start date of the quarters from current year and previous year by using the sysdate.
eg. Today falls in the 1st quarter of the year, therefore I only want to get the start date of 1st quarter of last year and this year.
If I'm on December (which is in the 4th quarter), I want to get the start dates of 8 quarters (4 from last year, 4 from this year.)
select b.dt,
to_number(to_char(SYSDATE, 'Q')),
to_number(to_char(b.dt, 'Q'))
from dual a,
(select add_months(trunc(ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -12),
'yyyy'),
(rownum - 1) * 3) dt
from all_objects
where rownum <= 8
and add_months(trunc(ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -12),
'yyyy'),
(rownum - 1) * 3) <= SYSDATE
and to_number(to_char(SYSDATE, 'Q')) >=
to_number(to_char(add_months(trunc(ADD_MONTHS(TRUNC(SYSDATE,
'MM'),
-12),
'yyyy'),
(rownum - 1) * 3),
'Q'))) b
This query only returns the start date of 1st quarter of last year. I expect to get the start date of the 1st quarter of this year as well.
Here's one option; see comments within the code.
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> with
2 -- this mimics SYSDATE
3 today (datum) as
4 (select date '&date_literal' from dual),
5 -- which quarter does DATUM belong to? Find 1st day in "this" and "previous" year
6 quart as
7 (select trunc(datum, 'yyyy') this,
8 trunc(add_months(datum, -12), 'yyyy') previous,
9 to_char(datum, 'q') quart from today)
10 -- the fina result
11 select add_months(this, (level - 1) * 3) result
12 from quart
13 connect by level <= quart
14 union all
15 select add_months(previous, (level - 1) * 3) result
16 from quart
17 connect by level <= quart;
Enter value for date_literal: 2019-03-24
RESULT
----------
01.01.2019
01.01.2018
SQL> /
Enter value for date_literal: 2019-08-13
RESULT
----------
01.01.2019
01.04.2019
01.07.2019
01.01.2018
01.04.2018
01.07.2018
6 rows selected.
SQL>

sql oracle ignore holidays

I am using this code to calculate the difference between two dates ignoring weekends:
SELECT To_date(SYSDATE) -
To_date('01.07.2014', 'DD.MM.YYYY')
- 2 * ( TRUNC(Next_day(To_date(SYSDATE) - 1, 'FRI'))
- TRUNC( Next_day(To_date('01.07.2014' , 'DD.MM.YYYY')
- 1, 'FRI')) ) / 7 AS DAYS_BETWEEN
FROM dual
I have another table called table1 in which the column "date" exists (its type is "DATE") in which all dates where a holiday is are written down.
Example table 1:
DATES
12.06.2011
19.06.2014
09.05.2013
...
I am trying to make my code check this table and that if one date is between the two dates above it makes -1 day in the output.
It should be easy if you divide it into following tasks:
Generate all the dates between the two given dates using Row Generator method as shown here.
Ignore the dates which are weekend, i.e. Saturdays and Sundays
Check whether the dates in the range are having any match in the holiday table.
The following row generator query will give you the total count of weekdays, i.e. not including Saturdays and Sundays:
SQL> WITH dates 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(weekday) weekday_count
7 FROM
8 (SELECT
9 CASE
10 WHEN TO_CHAR(date1+LEVEL-1, 'DY','NLS_DATE_LANGUAGE=AMERICAN')
11 NOT IN ('SAT', 'SUN')
12 THEN 1
13 ELSE 0
14 END weekday
15 FROM dates
16 CONNECT BY LEVEL <= date2-date1+1
17 )
18 /
WEEKDAY_COUNT
-------------
261
SQL>
Now, based on above row generator query, let's see a test case.
The following query will calculate the count of working days between 1st Jan 2014 and 31st Dec 2014 excluding the holidays as mentioned in the table.
The WITH clause is only to use it as tables, in your case you can simply use your holiday table.
SQL> WITH dates
2 AS (SELECT To_date('01/01/2014', 'DD/MM/YYYY') date1,
3 To_date('31/12/2014', 'DD/MM/YYYY') date2
4 FROM dual),
5 holidays
6 AS (SELECT To_date('12.06.2011', 'DD.MM.YYYY') holiday FROM dual UNION ALL
7 SELECT To_date('19.06.2014', 'DD.MM.YYYY') holiday FROM dual UNION ALL
8 SELECT To_date('09.05.2013', 'DD.MM.YYYY') holiday FROM dual),
9 count_of_weekdays
10 AS (SELECT SUM(weekday) weekday_count
11 FROM (SELECT CASE
12 WHEN To_char(date1 + LEVEL - 1, 'DY',
13 'NLS_DATE_LANGUAGE=AMERICAN')
14 NOT IN (
15 'SAT',
16 'SUN' ) THEN 1
17 ELSE 0
18 END weekday
19 FROM dates
20 CONNECT BY LEVEL <= date2 - date1 + 1)),
21 count_of_holidays
22 AS (SELECT Count(*) holiday_count
23 FROM holidays
24 WHERE holiday NOT BETWEEN To_date('01/01/2015', 'DD/MM/YYYY') AND
25 To_date('31/03/2015', 'DD/MM/YYYY'))
26 SELECT weekday_count - holiday_count as working_day_count
27 FROM count_of_weekdays,
28 count_of_holidays
29 /
WORKING_DAY_COUNT
-----------------
258
SQL>
There were total 261 weekdays, out of which there were 3 holidays in holiday table. So, total count of working days in the output is 261 - 3 = 258.
SELECT To_date(sysdate)- To_date('01.07.2014','DD.MM.YYYY')
- (2 * (to_char(To_date(sysdate), 'WW') - to_char(To_date('01.07.2014','DD.MM.YYYY'), 'WW'))) AS DAYS_BETWEEN
FROM dual

List all the months using oracle sql

Guys, is there any better way to list all the months other than this:
select to_char(add_months(to_date('01/01/1000', 'DD/MM/RRRR'), ind.l-1), 'MONTH') as month_descr,
ind.l as month_ind
from dual descr,
(select l
from (select level l
from dual
connect by level <= 12
)
) ind
order by 2;
ANSWER:
SELECT to_char(add_months(SYSDATE, (LEVEL-1 )),'MONTH') as months
FROM dual
CONNECT BY LEVEL <= 1
ONE MORE QUESTION SEE BELOW
Also I want to list the previous two years including the current year. I wrote this sql query. Let me know if there is anything better.
select extract(year from sysdate) - (level-1) as years
from dual
connect by level <=3
order by years
Not better, but just a bit cleaner:
SQL> select to_char(date '2000-12-01' + numtoyminterval(level,'month'),'MONTH') as month
2 from dual
3 connect by level <= 12
4 /
MONTH
---------
JANUARY
FEBRUARY
MARCH
APRIL
MAY
JUNE
JULY
AUGUST
SEPTEMBER
OCTOBER
NOVEMBER
DECEMBER
12 rows selected.
Regards,
Rob.
Yup.
1:
SELECT * FROM WWV_FLOW_MONTHS_MONTH;
2: (UPD:)
WITH MONTH_COUNTER AS (
SELECT LEVEL-1 AS ID
FROM DUAL
CONNECT BY LEVEL <= 12
)
SELECT TO_CHAR(ADD_MONTHS(TO_DATE('01/01/1000', 'DD/MM/RRRR'), ID),'MONTH') FROM MONTH_COUNTER;
select to_char(add_months(trunc(sysdate, 'yyyy'), level - 1), 'MONTH') months
from dual
connect by level <= 12;
Returns:
MONTHS
--------------------
JANUARY
FEBRUARY
MARCH
APRIL
MAY
JUNE
JULY
AUGUST
SEPTEMBER
OCTOBER
NOVEMBER
DECEMBER
12 rows selected.
SELECT to_char(to_date( level,'mm'), 'MONTH') Months FROM DUAL CONNECT BY LEVEL <=12;
Regards,
Prasant Sutaria
SELECT TO_CHAR(TO_DATE(rownum||'-'||rownum||'-'||'2013', 'DD-MM-YYYY'), 'Month')
FROM all_objects
WHERE rownum < 13