I have a situation where I need to write a SQL query to get last 10 years of data. For the first release date require last 10 years data and second release dates are mentioned in the picture. Need to automate dates according to the release date. Please help me.
enter image description here
For every release date, dates should automatically changes. Should not be hard-coded dates.
Maybe this could be helpfull:
WITH
release_dates AS -- sample data
( Select Add_Months(SYSDATE, (-10 + LEVEL)) - (LEVEL * 3) "RELEASE_DATE" From Dual Connect By LEVEL <= 9 )
SELECT
RELEASE_DATE,
--
ADD_MONTHS(
CASE WHEN To_Char(RELEASE_DATE, 'dd') <= 20 THEN LAST_DAY(ADD_MONTHS(RELEASE_DATE, -1))
ELSE LAST_DAY(RELEASE_DATE)
END + 1
, - 120) "START_DATE",
--
CASE WHEN To_Char(RELEASE_DATE, 'dd') <= 20 THEN LAST_DAY(ADD_MONTHS(RELEASE_DATE, -1))
ELSE LAST_DAY(RELEASE_DATE)
END "END_DATE",
--
To_Char(
ADD_MONTHS(
CASE WHEN To_Char(RELEASE_DATE, 'dd') <= 20 THEN LAST_DAY(ADD_MONTHS(RELEASE_DATE, -1))
ELSE LAST_DAY(RELEASE_DATE)
END + 1
, - 120)
, 'dd-MON-yy') || ' - ' ||
To_Char(RELEASE_DATE, 'dd-MON-yy')"DATA_REFRESH_DATES"
FROM
release_dates
R e s u l t :
RELEASE_DATE
START_DATE
END_DATE
DATA_REFRESH_DATES
18-MAR-22
01-MAR-12
28-FEB-22
01-MAR-12 - 18-MAR-22
15-APR-22
01-APR-12
31-MAR-22
01-APR-12 - 15-APR-22
12-MAY-22
01-MAY-12
30-APR-22
01-MAY-12 - 12-MAY-22
09-JUN-22
01-JUN-12
31-MAY-22
01-JUN-12 - 09-JUN-22
06-JUL-22
01-JUL-12
30-JUN-22
01-JUL-12 - 06-JUL-22
03-AUG-22
01-AUG-12
31-JUL-22
01-AUG-12 - 03-AUG-22
31-AUG-22
01-SEP-12
31-AUG-22
01-SEP-12 - 31-AUG-22
27-SEP-22
01-OCT-12
30-SEP-22
01-OCT-12 - 27-SEP-22
25-OCT-22
01-NOV-12
31-OCT-22
01-NOV-12 - 25-OCT-22
Related
I'm using Oracle 12c.
I need to generate dates for the start and end of weeks which begin on Thursday and end the following Wednesday.
An example of the output I'd like is -
I have the following SQL to generate the Start Date(s) -
SELECT startdate
FROM (SELECT next_day(date '2020-03-12' - 1, 'Thursday') + (level - 1) * 7 AS startdate
FROM dual
CONNECT BY level <=
((date'2024-03-31' - next_day(date '2020-03-12' - 1, 'Wednesday') + 7) / 7))
and this for End Dates -
(SELECT enddate
FROM (SELECT next_day(date '2020-03-12' - 1, 'Wednesday') + (level - 1) * 7 as enddate
FROM dual
CONNECT BY level <= ((date'2024-03-31' - next_day(date'2020-03-12' - 1, 'Thursday') + 7) / 7)))
Is it even possible to combine these in a single SQL query so the output of the query matches the desired format?
If so, then the addition of the week number would also be rather nice...:)
Generate the start date and then add 6 days to get the end date:
SELECT startdate,
startdate + INTERVAL '6' DAY AS enddate,
week
FROM (
SELECT NEXT_DAY(date'2020-03-12' - 1, 'Thursday')
+ ( level - 1 ) * INTERVAL '7' DAY as startdate,
LEVEL AS week
FROM DUAL
CONNECT BY
NEXT_DAY(date'2020-03-12' - 1, 'Thursday')
+ ( level - 1 ) * INTERVAL '7' DAY
+ INTERVAL '6' DAY
<= date'2024-03-31'
)
Which outputs:
STARTDATE
ENDDATE
WEEK
2020-03-12 00:00:00
2020-03-18 00:00:00
1
2020-03-19 00:00:00
2020-03-25 00:00:00
2
2020-03-26 00:00:00
2020-04-01 00:00:00
3
...
...
...
2024-03-07 00:00:00
2024-03-13 00:00:00
209
2024-03-14 00:00:00
2024-03-20 00:00:00
210
2024-03-21 00:00:00
2024-03-27 00:00:00
211
db<>fiddle here
I have one requirement
START_DATE : 03/01/2018
END_DATE : 28/12/2018
I need a query which will list all the months starting date and end date between these two dates like this
StartMonth EndMonth
03/01/2018 31/01/2018
01/02/2018 28/01/2018
01/03/2018 31/03/2018
01/04/2018 30/04/2018
01/05/2018 31/05/2018
01/06/2018 30/06/2018
01/07/2018 31/07/2018
01/08/2018 31/08/2018
01/09/2018 30/09/2018
01/10/2018 31/10/2018
01/11/2018 30/11/2018
01/12/2018 28/12/2018
I also need a query which will list all the Quarter's starting date and end date between these two dates like this
StartQuarter EndQuarter
03/01/2018 31/03/2018
01/04/2018 30/06/2018
01/07/2018 30/09/2018
01/10/2018 28/12/2018
These should work.
For months:
select greatest(:start_date, trunc(add_months( :start_date, level - 1), 'MON')) as startmonth,
least(:end_date, last_day(add_months( :start_date, level - 1))) as endmonth
from dual
connect by level <= trunc(months_between(trunc( :end_date, 'MON'), trunc( :start_date, 'MON'))) + 1;
For quarters:
select greatest(:start_date, trunc(add_months( :start_date, 3 * (level - 1)), 'Q')) as startmonth,
least(:end_date, last_day(add_months( :start_date, 3 * (level - 1) + 2))) as endmonth
from dual
connect by level <= trunc(months_between(trunc( :end_date, 'Q'), trunc( :start_date, 'Q'))) / 3 + 1;
Where :start_date and :end_date are your start and end dates.
Here is a CTE (Common Table Expression) that generates the monthly entries:
WITH
aset
AS
(SELECT DATE '2018-01-03' start_month, DATE '2018-12-28' last_month
FROM DUAL),
bset (start_month, end_month, last_month)
AS
(SELECT start_month, LEAST (ADD_MONTHS (TRUNC (start_month, 'MM'), 1) - 1, last_month) end_month, last_month
FROM aset
UNION ALL
SELECT ADD_MONTHS (TRUNC (start_month, 'MM'), 1)
, LEAST (ADD_MONTHS (start_month, 2) - 1, last_month)
, last_month
FROM bset
WHERE end_month < last_month)
SELECT start_month, end_month
FROM bset;
START_MONTH END_MONTH
----------- ---------
03-JAN-18 31-JAN-18
01-FEB-18 02-MAR-18
01-MAR-18 31-MAR-18
01-APR-18 30-APR-18
01-MAY-18 31-MAY-18
01-JUN-18 30-JUN-18
01-JUL-18 31-JUL-18
01-AUG-18 31-AUG-18
01-SEP-18 30-SEP-18
01-OCT-18 31-OCT-18
01-NOV-18 30-NOV-18
01-DEC-18 28-DEC-18
Here's the variant that's not limited to any specific range length:
SELECT TO_CHAR(GREATEST(
LAST_DAY(ADD_MONTHS(:start_date, LEVEL - 2)) + 1,
:start_date
), 'dd/mm/yyyy') AS StartMonth,
TO_CHAR(LEAST(
:end_date,
LAST_DAY(ADD_MONTHS(:start_date, LEVEL - 1))
), 'dd/mm/yyyy') AS EndMonth
FROM dual
CONNECT BY LAST_DAY(ADD_MONTHS(:start_date, LEVEL - 1)) <= LAST_DAY(:end_date);
EDIT: after fixing the start date range, it looks very much like the accepted answer, with different way of doing date arithmetic. And for quarterly, #GriffeyDog's usage of TRUNC is probably the best bet still.
I have an Oracle Sql query which gives a list of months in a financial year. I want to convert to postgresql as it doesn't have level
select case
when to_char(add_months(sysdate , -1), 'MM') >= 4 then
to_char(add_months(sysdate , -1), 'YYYY') || '-' ||
to_char(to_number(to_char(add_months(sysdate , -1), 'YYYY')) + 1)
else
to_char(to_number(to_char(add_months(sysdate , -1), 'YYYY')) - 1) || '-' ||
to_char(add_months(sysdate , -1), 'YYYY')
end FY,
to_char(level, '00') MNTH
from dual
connect by level <=12
select
case when date_part('month',current_date) >= 4 then
concat(date_part('year', current_date), '-', date_part('year',current_date)+1)
else
concat(date_part('year', current_date)-1, '-', date_part('year',current_date))
end FY
, lpad(date_part('month',d)::varchar,2,'0') MNTH
from (select DATE '2008-01-01' + (interval '1' month * generate_series(0,11)) d ) y
current_date (instead of sysdate)
+ Interval 'n Month' (instead of add_months)
generate series (instead of connect by )
date_part('month',... to get the month number (instead of to_char(...,'MM') )
concat() for concatenations (looks after type conversions too)
sample result:
FY MNTH
1 2017-2018 01
2 2017-2018 02
3 2017-2018 03
4 2017-2018 04
5 2017-2018 05
6 2017-2018 06
7 2017-2018 07
8 2017-2018 08
9 2017-2018 09
10 2017-2018 10
11 2017-2018 11
12 2017-2018 12
I would like to get a table of months between two dates with a fraction of each month that the two dates cover.
For example with a start date of 15/01/2017 and end date of 01/03/2017 it would output:
01/2017 : 0.5483..
02/2017 : 1
03/2017: 0.0322..
where for January and March the calculations are 17/31 and 1/31 respectively. I currently have the query:
WITH dates_between as (SELECT ADD_MONTHS(TRUNC(TO_DATE(:givenStartDate,'dd/mm/yyyy'), 'MON'), ROWNUM - 1) date_out
FROM DUAL
CONNECT BY ADD_MONTHS(TRUNC(TO_DATE(:givenStartDate,'dd/mm/yyyy'), 'MON'), ROWNUM - 1)
<= TRUNC(TO_DATE(:givenEndDate,'dd/mm/yyyy'), 'MON')
)
select * from dates_between
This outputs each month between two dates and formats it to the start of the month. I just need another column to give me the fraction the start and end dates cover. I'm not sure of a way to do this without it getting messy.
The months_between() function "calculates the fractional portion of the result based on a 31-day month". That means that if your range starts or ends in a month that doesn't have 31 days, the fraction you get might not be quite what you expect:
select months_between(date '2017-04-02', date '2017-04-01') as calc from dual
CALC
----------
.0322580645
... which is 1/31, not 1/30. To get 0.0333... instead you'd need to calculate the number of days in each month, at least for the first and last month. This uses a recursive CTE (11gR2+) to get the months, using a couple of date ranges provided by another CTE as a demo to show the difference (you can use a hierarchical query too of course):
with ranges (id, start_date, end_date) as (
select 1, date '2017-01-15', date '2017-03-01' from dual
union all select 2, date '2017-01-31', date '2017-03-01' from dual
union all select 3, date '2017-02-28', date '2017-04-01' from dual
),
months (id, month_start, month_days, range_start, range_end) as (
select id,
trunc(start_date, 'MM'),
extract(day from last_day(start_date)),
start_date,
end_date
from ranges
union all
select id,
month_start + interval '1' month,
extract(day from last_day(month_start + interval '1' month)),
range_start,
range_end
from months
where month_start < range_end
)
select id,
to_char(month_start, 'YYYY-MM-DD') as month_start,
month_days,
case when month_start = trunc(range_start, 'MM')
then month_days - extract(day from range_start) + 1
when month_start = trunc(range_end, 'MM')
then extract(day from range_end)
else month_days end as range_days,
(case when month_start = trunc(range_start, 'MM')
then month_days - extract(day from range_start) + 1
when month_start = trunc(range_end, 'MM')
then extract(day from range_end)
else month_days end) / month_days as fraction
from months
order by id, month_start;
which gets:
ID MONTH_STAR MONTH_DAYS RANGE_DAYS FRACTION
------ ---------- ---------- ---------- --------
1 2017-01-01 31 17 0.5483
1 2017-02-01 28 28 1
1 2017-03-01 31 1 0.0322
2 2017-01-01 31 1 0.0322
2 2017-02-01 28 28 1
2 2017-03-01 31 1 0.0322
3 2017-02-01 28 1 0.0357
3 2017-03-01 31 31 1
3 2017-04-01 30 1 0.0333
The first CTE ranges is just the demo data. The second, recursive, CTE months generates the start and number of days in each month, while keeping track of the original range dates too. The final query just calculates the fractions based on the number of days in the month in the range against the number of days in that month overall.
The month_days and range_days are only shown in the output so you can see what the calculation is based on, you can obviously omit those from your actual result, and format the month start date however you want.
With your original single pair of bind variables the equivalent would be:
with months (month_start, month_days, range_start, range_end) as (
select trunc(to_date(:givenstartdate, 'DD/MM/YYYY'), 'MM'),
extract(day from last_day(to_date(:givenstartdate, 'DD/MM/YYYY'))),
to_date(:givenstartdate, 'DD/MM/YYYY'),
to_date(:givenenddate, 'DD/MM/YYYY')
from dual
union all
select month_start + interval '1' month,
extract(day from last_day(month_start + interval '1' month)),
range_start,
range_end
from months
where month_start < range_end
)
select to_char(month_start, 'MM/YYYY') as month,
(case when month_start = trunc(range_start, 'MM')
then month_days - extract(day from range_start) + 1
when month_start = trunc(range_end, 'MM')
then extract(day from range_end)
else month_days end) / month_days as fraction
from months
order by month_start;
MONTH FRACTION
------- --------
01/2017 0.5483
02/2017 1
03/2017 0.0322
Here's how I would do it (n.b. I have expanded your dates_between to work against multiple rows, purely for demonstration purposes. If you're only working with a single set of parameters, you wouldn't need to do that):
WITH params AS (SELECT 1 ID, '15/01/2017' givenstartdate, '01/03/2017' givenenddate FROM dual UNION ALL
SELECT 2 ID, '15/01/2017' givenstartdate, '23/01/2017' givenenddate FROM dual UNION ALL
SELECT 3 ID, '01/01/2017' givenstartdate, '07/04/2017' givenenddate FROM dual),
dates_between AS (SELECT ID,
to_date(givenstartdate, 'dd/mm/yyyy') givenstartdate,
to_date(givenenddate, 'dd/mm/yyyy') givenenddate,
add_months(trunc(to_date(givenstartdate, 'dd/mm/yyyy'), 'MON'), LEVEL - 1) start_of_month,
last_day(add_months(trunc(to_date(givenstartdate, 'dd/mm/yyyy'), 'MON'), LEVEL - 1)) end_of_month
FROM params
CONNECT BY add_months(trunc(to_date(givenstartdate, 'dd/mm/yyyy'), 'MON'), LEVEL - 1) <=
trunc(to_date(givenenddate, 'dd/mm/yyyy'), 'MON')
AND PRIOR ID = ID
AND PRIOR sys_guid() IS NOT NULL)
SELECT ID,
givenstartdate,
givenenddate,
start_of_month date_out,
end_of_month,
months_between(LEAST(givenenddate, end_of_month) + 1, GREATEST(start_of_month, givenstartdate))
FROM dates_between;
ID GIVENSTARTDATE GIVENENDDATE DATE_OUT END_OF_MONTH DIFF
1 15/01/2017 01/03/2017 01/01/2017 31/01/2017 0.54838709
1 15/01/2017 01/03/2017 01/02/2017 28/02/2017 1
1 15/01/2017 01/03/2017 01/03/2017 31/03/2017 0.03225806
2 15/01/2017 23/01/2017 01/01/2017 31/01/2017 0.29032258
3 01/01/2017 07/04/2017 01/01/2017 31/01/2017 1
3 01/01/2017 07/04/2017 01/02/2017 28/02/2017 1
3 01/01/2017 07/04/2017 01/03/2017 31/03/2017 1
3 01/01/2017 07/04/2017 01/04/2017 30/04/2017 0.22580645
N.B. You may need to add a case statement to decide whether you want to add 1 or not to the diff calculation, based on your requirements.
Try this
For first month, I have calculated remaining days / total days and for last month, I subtracted it by 1 to get days passed / total days.
DBFiddle Demo
WITH tbl AS
(SELECT date '2017-01-15' AS givenStartDate
,date '2017-03-01' AS givenEndDate
FROM dual
)
SELECT ADD_MONTHS(TRUNC(givenStartDate, 'MON'), ROWNUM - 1) AS date_out ,
CASE
WHEN
rownum - 1 = 0
THEN months_between(last_day(givenStartDate), givenStartDate)
WHEN ADD_MONTHS(TRUNC(givenStartDate, 'MON'), ROWNUM - 1) = TRUNC(givenEndDate, 'MON')
THEN 1 - (months_between(last_day(givenEndDate), givenEndDate))
ELSE 1
END AS perc
FROM tbl
CONNECT BY ADD_MONTHS(TRUNC(givenStartDate, 'MON'), ROWNUM - 1)
<= TRUNC(givenEndDate, 'MON');
Output
+-----------+-------------------------------------------+
| DATE_OUT | PERC |
+-----------+-------------------------------------------+
| 01-JAN-17 | .5161290322580645161290322580645161290323 |
| 01-FEB-17 | 1 |
| 01-MAR-17 | .0322580645161290322580645161290322580645 |
+-----------+-------------------------------------------+
I have a table called fiscal year with column start_date,end_date(empty table), I want to insert records for each fiscal year till 2060
FISCAL_YEAR Start dt is Jul 1st, End dt is Jun 31st of next year
what i tried
select add_months(start_date ,-6),add_months(start_date ,6)-1 from (
select to_date('20000101','yyyymmdd') start_date from dual )
basis
how do i generate this sequence till 2060
Decription start_date end_date
FISCAL YEAR 2000 7/1/1999 6/30/2000
SQL> select
2 to_date('01-07-' || (1999 + rownum), 'dd.mm.yyyy') start_date,
3 to_date('30-06-' || (2000 + rownum), 'dd.mm.yyyy') finish_date
4 from dual
5 connect by level <= 10;
START_DATE FINISH_DATE
----------- -----------
01.07.2000 30.06.2001
01.07.2001 30.06.2002
01.07.2002 30.06.2003
01.07.2003 30.06.2004
01.07.2004 30.06.2005
01.07.2005 30.06.2006
01.07.2006 30.06.2007
01.07.2007 30.06.2008
01.07.2008 30.06.2009
01.07.2009 30.06.2010
10 rows selected
You can do it like this:
SELECT
ADD_MONTHS(DATE '1999-07-01', 12*(LEVEL-1)) as fiscal_year_begin,
ADD_MONTHS(DATE '1999-07-01', 12*LEVEL) - INTERVAL '1' DAY AS fiscal_year_end
FROM dual
CONNECT BY LEVEL < 60;
FISCAL_YEAR_BEGIN FISCAL_YEAR_END
1999-07-01 2000-06-30
2000-07-01 2001-06-30
2001-07-01 2002-06-30
2002-07-01 2003-06-30
2003-07-01 2004-06-30
2004-07-01 2005-06-30
2005-07-01 2006-06-30
2006-07-01 2007-06-30
2007-07-01 2008-06-30
2008-07-01 2009-06-30
...