Problem with getting the quarter from a date in Oracle - sql

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>

Related

Get count days in current year by day name or day id

name id
----------------
Mon 1
Thu 2
Wen 3
Thr 4
Fri 5
Sat 6
San 7
How get count day where id in eg. (1,2,3,4) and year is 2021
The result should be 208.
Actually, it is 208 for year 2021.
SQL> WITH
2 year AS (SELECT &par_year year FROM DUAL),
3 calendar
4 AS
5 ( SELECT TRUNC (TO_DATE (y.year, 'yyyy'), 'yyyy') + LEVEL - 1 datum
6 FROM year y
7 CONNECT BY LEVEL <=
8 ADD_MONTHS (TRUNC (TO_DATE (y.year, 'yyyy'), 'yyyy'), 12)
9 - TRUNC (TO_DATE (y.year, 'yyyy'), 'yyyy'))
10 SELECT SUM (CASE
11 WHEN TO_NUMBER (TO_CHAR (c.datum, 'd')) IN (1,
12 2,
13 3,
14 4)
15 THEN
16 1
17 ELSE
18 0
19 END) result
20 FROM calendar c;
Enter value for par_year: 2021
RESULT
----------
208
SQL> /
Enter value for par_year: 2020
RESULT
----------
210
SQL>
What does it do?
YEAR CTE contains year you're interested in
CALENDAR CTE creates all dates in that particular year
SUM function conditionally adds 1 if TO_CHAR(datum, 'd') is 1, 2, 3 or 4
that's all

Generating Rows for each date from start date to end date [duplicate]

This question already has answers here:
Row for each date from start date to end date
(2 answers)
Generate series of months for every row in Oracle
(1 answer)
Create all months list from a date column in ORACLE SQL
(3 answers)
Closed 2 years ago.
What I'm trying to do is take a record that looks like this:
Start_DT End_DT ID
4/5/2013 10/9/2013 1
and change it to look like this:
DT ID
4/1/2013 1
5/1/2013 1
6/1/2013 1
7/1/2013 1
8/1/2013 1
9/1/2013 1
10/1/2013 1
I am having difficult time making this work. Any help would be appreciated.
Thanks
You can use a recursive CTE for this:
with dates (dte, end_mon, id) as (
select trunc(start_dt, 'MON') as dte, trunc(end_dt, 'MON') as end_mon, id
from t
union all
select dte + interval '1' month, end_mon, id
from dates
where dte < end_mon
)
select *
from dates;
Here is a db<>fiddle.
Alternatively, but similarly - row generator technique is what you're looking for.
(My date format is DD.MM.YYYY; can't tell for yours so I'm just guessing as both of your dates can be read in two ways, e.g. 4/5/2013 - is it 4th of May or 5th of April?).
SQL> with dates (start_dt, end_dt, id) as
2 (select date '2013-05-04', date '2013-09-10', 1 from dual)
3 select start_dt + level - 1 dt, id
4 from dates
5 connect by level <= end_dt - start_dt + 1
6 order by dt;
DT ID
---------- ----------
04.05.2013 1
05.05.2013 1
06.05.2013 1
07.05.2013 1
08.05.2013 1
<snip>
06.09.2013 1
07.09.2013 1
08.09.2013 1
09.09.2013 1
10.09.2013 1
130 rows selected.
SQL>
This can be achieved by using CONNECT BY to generate the months between each date.
Query
WITH
dates (start_date, end_date, id)
AS
(SELECT TO_DATE ('4/5/2013', 'MM/DD/YYYY'), TO_DATE ('10/9/2013', 'MM/DD/YYYY'), 1
FROM DUAL)
SELECT TO_CHAR (ADD_MONTHS (TRUNC (start_date, 'MM'), LEVEL - 1), 'MM/DD/YYYY') AS dt, id
FROM dates
CONNECT BY ADD_MONTHS (TRUNC (start_date, 'MM'), LEVEL - 1) <= TRUNC (end_date, 'MM');
Result
DT ID
_____________ _____
04/01/2013 1
05/01/2013 1
06/01/2013 1
07/01/2013 1
08/01/2013 1
09/01/2013 1
10/01/2013 1

Dynamically generate last 36 month dates

How to generate list of dates dynamically for last 36 months in SQL
As of Oracle:
select add_months(trunc(sysdate), -36) + level - 1 datum
from dual
connect by level <= trunc(sysdate) - add_months(trunc(sysdate), -36) + 1
order by datum;
DATUM
----------
29.04.2017
30.04.2017
01.05.2017
02.05.2017
03.05.2017
04.05.2017
<snip>
26.04.2020
27.04.2020
28.04.2020
29.04.2020
367 rows selected.
with t (dt1, dt2) as (
select trunc(sysdate, 'month') - 1, last_day(trunc(sysdate)) + 1 - interval '36' month from dual
)
select dt2 + level - 1
from t
connect by
dt2 + level - 1 <= dt1
;
trunc(sysdate, 'month') gives you the first day of the month at 00 hours.
last_day(trunc(sysdate)) gives you the last day of the month at 00 hours.

How can I generate 4-week ranges of dates?

I have to generate date range for 1 year in gap of 4 weeks ( or 28 days ) from a fixed date backward and going forward. For example I have DATE '2016-02-20'. I need to generate the below.
Start date = Sunday , End-date = Saturday
No Start_date End_date
==== ========= =======
1 1/24/2016 2/20/2016
2 12/27/2015 1/23/2016
3 11/29/2015 12/26/2015
4 .....
13 2/22/2015 3/21/2015
14 1/25/2015 2/21/2015
But,when 03/20/2016(Sunday) comes,it should add
1. 2/21/2016 3/19/2016 & remove 14. 1/25/2015 2/21/2015
and so on after every 4 weeks.
I have written the below, but I need help to iterate in minimal code( if possible.)
SELECT LEVEL,
DATE '2016-02-20'-27*LEVEL-LEVEL+1 AS start_date,
DATE '2016-02-20'-28*(LEVEL-1) AS end_date
FROM DUAL
Connect BY LEVEL < 15;
It seems you want to have a rolling window of a year's worth of four-week ranges, based from the current date. To do that you need a fixed known period start (or end) date you can work from. Picking one that happens to be January 1st you can do:
SELECT DATE '2012-01-01' + (28 * (LEVEL - 1)) AS start_date,
DATE '2012-01-01' + (28 * LEVEL) - 1 AS end_date
FROM DUAL
CONNECT BY DATE '2012-01-01' + (28 * LEVEL) - 1 <= TRUNC(sysdate)
Which will find 54 periods up to today. On March 21st it will find 55 periods, etc. You only want those that are in the last year, so use that as an inline view and restrict the range:
SELECT ROW_NUMBER() OVER (ORDER BY start_date DESC) AS no, start_date, end_date
FROM (
SELECT DATE '2012-01-01' + (28 * (LEVEL - 1)) AS start_date,
DATE '2012-01-01' + (28 * LEVEL) - 1 AS end_date
FROM DUAL
CONNECT BY DATE '2012-01-01' + (28 * LEVEL) - 1 <= TRUNC(sysdate)
)
WHERE end_date >= ADD_MONTHS(TRUNC(sysdate), -12)
ORDER BY start_date DESC;
NO START_DATE END_DATE
---------- ---------- ----------
1 01/24/2016 02/20/2016
2 12/27/2015 01/23/2016
3 11/29/2015 12/26/2015
...
11 04/19/2015 05/16/2015
12 03/22/2015 04/18/2015
13 02/22/2015 03/21/2015
The ROW_NUMBER() just generates your NO column, as the LEVEL is now in the wrong order.
If you always want exactly 14 rows in the result set you can move the ROW_NUMBER() into the inline view:
SELECT no, start_date, end_date
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY LEVEL DESC) AS no,
DATE '2012-01-01' + (28 * (LEVEL - 1)) AS start_date,
DATE '2012-01-01' + (28 * LEVEL) - 1 AS end_date
FROM DUAL
CONNECT BY DATE '2012-01-01' + (28 * LEVEL) - 1 <= TRUNC(sysdate)
)
WHERE no <= 14
ORDER BY no;
NO START_DATE END_DATE
---------- ---------- ----------
1 01/24/2016 02/20/2016
2 12/27/2015 01/23/2016
3 11/29/2015 12/26/2015
...
12 03/22/2015 04/18/2015
13 02/22/2015 03/21/2015
14 01/25/2015 02/21/2015
14 rows selected

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