Current Financial Year to sysdate - sql

I can't seem to find a straightforward sql without delving into PL SQL for always bringing current financial year in which case 01-04-2015 to sysdate. I want this to always update automatically so when it comes next financial year in 01/04/2016 it will bring whatever is held from that date to whenever the report is being run.
If anyone can please shed some light for me. thanks
sql is:
SELECT
PROPERTY.PRO_MANAGINGCOMPANY_DESCR,
PROPERTY.PRO_SCHEME_DESCR,
PROPERTY.PRO_SCHEME,
SUM(REPAIR_CURRENT.REP_ESTIMATED_COST) as "Estimated Cost",
nvl(SUM(REPAIR_CURRENT.REP_INVOICED_COST),SUM(REPAIR_CURRENT.REP_ESTIMATED_COST)) as "Estimated Cost Invoiced",
SUM(REPAIR_CURRENT.REP_INVOICED_COST) as "Invoice Cost",
to_char(REPAIR_CURRENT.REP_RAISED_DATE,'Mon') as "Month",
to_number(to_char(to_date(REPAIR_CURRENT.REP_RAISED_DATE,'dd-mon-yy'),'mm')) as "Month No."
FROM
PROPERTY,
REPAIR_CURRENT,
SERVICE_REQUEST
WHERE
( SERVICE_REQUEST.SRQ_PRO_REFNO=PROPERTY.PRO_REFNO )
AND ( REPAIR_CURRENT.REP_SRQ_NO=SERVICE_REQUEST.SRQ_NO )
AND
(
--PROPERTY.PRO_SCHEME = ( '00054' )
--AND
REPAIR_CURRENT.REP_RAISED_DATE BETWEEN '01-APR-2015' AND sysdate
AND
REPAIR_CURRENT.REP_STATUS <> 'CAN'
)
GROUP BY
PROPERTY.PRO_MANAGINGCOMPANY_DESCR,
PROPERTY.PRO_SCHEME_DESCR,
PROPERTY.PRO_SCHEME,
to_char(REPAIR_CURRENT.REP_RAISED_DATE,'Mon'),
to_number(to_char(to_date(REPAIR_CURRENT.REP_RAISED_DATE,'dd-mon-yy'),'mm'))

If you just want to get the beginning of the fiscal year for the current date:
SELECT TO_DATE('01-04' || CASE
WHEN EXTRACT(MONTH FROM SYSDATE) > 4 THEN
EXTRACT(YEAR FROM SYSDATE)
ELSE
EXTRACT(YEAR FROM SYSDATE)-1
END, 'DD-MM-RRRR') FISCAL_YEAR
FROM DUAL

This works for any date:
REPAIR_CURRENT.REP_RAISED_DATE
BETWEEN Add_Months(Trunc(Add_Months(sysdate,-3),'YYYY'),3)
AND Sysdate
Basically, subtract three months, truncate to the year, and add three months back on.
To just get the financial year for a date, use:
Extract(Year from Add_Months(Trunc(Add_Months(sysdate,-3),'YYYY'),3))

SELECT *
FROM your_table
WHERE datetime >= CASE
WHEN SYSDATE < TRUNC( SYSDATE, 'YEAR' ) + INTERVAL '3' MONTH
THEN TRUNC( SYSDATE, 'YEAR' ) - INTERVAL '9' MONTH
ELSE TRUNC( SYSDATE, 'YEAR' ) + INTERVAL '3' MONTH
END;

Thank you, the following worked! add_months(trunc(sysdate,'year'),3) AND sysdate
thank you all for your input :)

REPAIR_CURRENT.REP_RAISED_DATE BETWEEN '01-APR-2015' AND sysdate
Firstly, '01-APR-2015' is not a DATE it is a string. You must always use TO_DATE along with proper format model to explicitly convert the string into DATE. Or, use the ANSI Date literal as you are not concerned with the time portion. It uses a fixed format 'YYYY-MM-DD'.
Now, coming to your date arithmetic, you could use a CASE expression to evaluate the financial date depending on the year.
REP_RAISED_DATE
BETWEEN
CASE
WHEN
SYSDATE < ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3)
THEN
ADD_MONTHS(TRUNC(SYSDATE, 'YEAR') , -9)
ELSE
ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3)
END
AND SYSDATE
Basically, SYSDATE >= ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3) is to check whether SYSDATE is greater than 1-APR of current year. And, SYSDATE < ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 15) is to check whether it is between JAN and MARCH of next year.
For example,
SQL> SELECT
2 CASE
3 WHEN
4 SYSDATE < ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3)
5 THEN
6 ADD_MONTHS(TRUNC(SYSDATE, 'YEAR') ,-9)
7 ELSE
8 ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3)
9 END FINANCIAL_YEAR
10 FROM dual;
FINANCIAL
---------
01-APR-15
For date between JAN and MAR of next year:
SQL> SELECT
2 CASE
3 WHEN
4 DATE '2016-02-01' < ADD_MONTHS(TRUNC(DATE '2016-02-01', 'YEAR'), 3)
5 THEN
6 ADD_MONTHS(TRUNC(DATE '2016-02-01', 'YEAR') ,-9)
7 ELSE
8 ADD_MONTHS(TRUNC(DATE '2016-02-01', 'YEAR'), 3)
9 END FINANCIAL_YEAR
10 FROM dual;
FINANCIAL
---------
01-APR-15

Following SQLreturns start and end date for Financial Year of current date.
SELECT
TO_DATE('01-04' || EXTRACT(YEAR FROM add_months(sysdate, -3)),'DD-MM-RRRR') from_dt ,
TO_DATE('31-03' || EXTRACT(YEAR FROM add_months(sysdate, 9)),'DD-MM-RRRR') to_dt from dual;
For any random date, you can use the following SQL: example for 01-04-2020
SELECT
TO_DATE('01-04' || EXTRACT(YEAR FROM add_months(to_date('01-04-2020','DD-MM-RRRR'), -3)),'DD-MM-RRRR') from_dt ,
TO_DATE('31-03' || EXTRACT(YEAR FROM add_months(to_date('01-04-2020','DD-MM-RRRR'), 9)),'DD-MM-RRRR') to_dt from dual;

Related

How to generate the first day of the week, the last day of the week and week number between two dates in Oracle

I would like to insert in table :
the first day of the week starting from Monday.
the last day of the week as Sunday.
the WEEK number => (1-52 or 1-53) based on the ISO standard.
First i tried to select first day, the last day and week number for specific date and it's works :
WITH ranges AS
(
SELECT to_date('29-10-2012', 'dd-MM-yyyy') AS DATE_TEST FROM DUAL
)
SELECT DATE_TEST "DATE",
TO_CHAR( NEXT_DAY( TRUNC(DATE_TEST) , 'SUNDAY' )) "WEEK END DATE",
TO_CHAR(TO_DATE(DATE_TEST,'DD-MON-YYYY'),'WW')+1 "WEEK NUMBER"
FROM ranges ;
But now i would like to display this data between two dates, but i get result only for the start_date. someone can help please.
after, when all good i will insert all in the table.
Thanks
WITH ranges AS(
select to_date('29-OCT-2012', 'dd-MM-yyyy') START_DATE,
to_date('31-DEC-2016', 'dd-MM-yyyy') END_DATE
from dual
)
SELECT START_DATE "DATE",
TO_CHAR( NEXT_DAY( TRUNC(START_DATE) , 'SUNDAY' )) "WEEK END DATE",
TO_CHAR(TO_DATE(START_DATE,'DD-MON-YYYY'),'WW')+1 "WEEK NUMBER"
FROM ranges ;
Format WW returns Week of year (1-53) where week 1 starts on the first day of the year and continues to the seventh day of the year, see Datetime Format Elements
In order to get the week number according to ISO-8601 standard use format IW. I would suggest like this:
WITH ranges AS(
SELECT
DATE '2012-10-29' START_DATE,
DATE '2016-12-31' END_DATE
FROM dual
)
SELECT
START_DATE, END_DATE,
TRUNC(START_DATE + 7*(LEVEL-1), 'IW') AS Week_Start_Date,
TRUNC(START_DATE + 7*(LEVEL-1), 'IW') + 6 AS Week_End_Date,
TO_CHAR(TRUNC(START_DATE + 7*(LEVEL-1)), 'IYYY-"W"IW') WEEK_NUMBER
FROM ranges
CONNECT BY START_DATE + 7*(LEVEL-1) <= END_DATE;
Looks like you're looking for a calendar.
Based on your RANGES CTE, there's another - calendar which utilizes hierarchical query to create all dates between start_date and end_date. Once you have all dates, extract values you're interested in.
SQL> with
2 ranges as
3 (select to_date('29-OCT-2012', 'dd-MM-yyyy') start_date,
4 to_date('31-DEC-2016', 'dd-MM-yyyy') end_date
5 from dual
6 ),
7 calendar as
8 (select start_date + level - 1 as datum
9 from ranges
10 connect by level <= end_date - start_date + 1
11 )
12 select
13 min(datum) start_date,
14 min(next_day(datum, 'sunday')) week_end_date,
15 to_char(datum, 'ww') week_number
16 from calendar
17 group by to_char(datum, 'yyyy'), to_char(datum, 'ww')
18 order by 1;
START_DATE WEEK_END_D WE
---------- ---------- --
29-10-2012 04-11-2012 44
04-11-2012 11-11-2012 45
11-11-2012 18-11-2012 46
18-11-2012 25-11-2012 47
25-11-2012 02-12-2012 48
<snip>
09-12-2016 11-12-2016 50
16-12-2016 18-12-2016 51
23-12-2016 25-12-2016 52
30-12-2016 01-01-2017 53
222 rows selected.
SQL>
How are you?
to help, I tried to do some research, I found these links.
enter link description here
enter link description here
I found:
SELECT
ROUND((TRUNC(SYSDATE) - TRUNC(SYSDATE, 'YEAR')) / 7,0) CANTWEEK,
NEXT_DAY(SYSDATE, 'SUNDAY') - 7 FIRSTDAY,
NEXT_DAY(SYSDATE, 'SUNDAY') - 1 LASTDAY
FROM DUAL
and
select
to_char(sysdate - to_char(sysdate, 'd') + 2, 'yyyymmdd') first_day_of_week
, to_char(sysdate - to_char(sysdate, 'd') + 8, 'yyyymmdd') last_day_of_week
from
dual
and
select sysdate AS today,
TRUNC(next_day(sysdate,'MONDAY')-8) as DOMINGO,
TRUNC(next_day(sysdate,'SATURDAY')) as SABADO
from dual
I don't have oracle here so I couldn't test it well but it should solve what you need, anything let me know :)
Here is a way to make the common table expression return a range of dates.
with ranges (dt) as (
select to_date('29-OCT-2012', 'dd-MM-yyyy') as dt
from dual
union all
select ranges.dt+1
from ranges
where ranges.dt < to_date('31-DEC-2016', 'dd-MM-yyyy')
)
Then you can use that to calculate the other values.
SELECT dt "DATE"
, TO_CHAR(NEXT_DAY(TRUNC(dt), 'SUNDAY')) "WEEK END DATE"
, TO_CHAR(TO_DATE(dt,'DD-MON-YYYY'),'WW') + 1 "WEEK NUMBER"
, cast(TO_CHAR(dt, 'WW') as int) +
case when cast(TO_CHAR(dt, 'D') as int) < cast(TO_CHAR(trunc(dt, 'year'), 'D') as int) then 1 else 0 end WeekNumberInYear
FROM ranges ;
If you want to have all of your date calculations done at once, check out an example here: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=35e407af3b5bf711bb7ae53b8cf0e608

How to put Case in Where Statement for Oracle SQL

For the query below, I'm trying to pull a specific date range depending on the current day of the month. If it's the 20th or less (e.g. "2/7/2020") then I want the date range for January. Otherwise, I want the date range for February. Is it possible to be done with a case statement? Or there is a better way?
SELECT
account,
start_date,
amount
FROM
table1
WHERE
CASE
WHEN (
SELECT
CAST(EXTRACT(DAY FROM sysdate) AS NUMBER)
FROM
dual
) <= 20 THEN
start_date
BETWEEN '2020-01-01' AND '2020-01-31'
ELSE start_date BETWEEN '2020-02-01' AND '2020-02-29'
END
You can do this by avoiding the case statement and using truncate the date - 20 to the month, e.g.:
SELECT account,
start_date,
amount
FROM table1
WHERE start_date >= TRUNC(SYSDATE - 20, 'mm')
AND start_date < add_months(TRUNC(dt - 20, 'mm'), 1);
If you really had to use a CASE expression (you can't use a CASE statement in SQL), you would need to do something like:
SELECT account,
start_date,
amount
FROM table1
WHERE start_date >= CASE WHEN to_char(SYSDATE, 'dd') <= '20' THEN add_months(TRUNC(SYSDATE, 'mm'), -1) ELSE TRUNC(SYSDATE, 'mm') END
AND start_date < CASE WHEN to_char(SYSDATE, 'dd') <= '20' THEN TRUNC(SYSDATE, 'mm') ELSE add_months(TRUNC(SYSDATE, 'mm'), 1) END;
N.B. if you're using a function, you don't need to wrap it in a select .. from dual, you can use it directly in the SQL statement.
I've also assumed that you want a dynamic range, e.g. if the day of the month is 20 or less, the range is for the previous month, otherwise the current month.
ETA: You would use the above two queries if there is an index on the start_date column, otherwise you could simply do:
SELECT account,
start_date,
amount
FROM table1
WHERE TRUNC(start_date, 'mm') = TRUNC(SYSDATE - 20, 'mm');
Case statements return single values. As such you should pull out the start date and you'll need two case statements.
select account, start_date, amount
from table1 where
start_date between
(case
when (select cast(extract(day from sysdate) as number) from dual) <= 20 then '2020-01-01'
else '2020-02-01'
end) and
(case
when (select cast(extract(day from sysdate) as number) from dual) <= 20 then '2020-01-31'
else '2020-02-29'
end)
One method subtracts 20 days and then gets the month boundary:
where start_date >= trunc(sysdate - interval '20' day, 'MON') and
start_date < trunc(sysdate - interval '20' day, 'MON') + interval '1' month
This approach is index (and partition) friendly -- an appropriate index on start_date can be used. It is also safe if start_date has time components.
Note: You can use sysdate without having to use a subquery.
You can use or operator with last_day function as following:
Select * from your_table
Where (
start_date <= trunc(sysdate,'mm') + 20
and start_date between trunc(sysdate,'mm') - interval '1' month and trunc(sysdate,'mm') - 1
)
Or
(
start_date > trunc(sysdate,'mm') + 20
and start_date between trunc(sysdate, 'mm') and last_day(sysdate)
)
This approach will use index on start_date, if any.
Cheers!!
select account, amount, start_date
from table1
where ( ( (select cast (extract (day from sysdate) as number) from dual) <= 20
and start_date between date '2020-01-01' and date '2020-01-31')
or ( (select cast (extract (day from sysdate) as number) from dual) > 20
and start_date between date '2020-02-01' and date '2020-02-29')
);
Using CASE expressions as BETWEEN operands:
SELECT account
, start_date
, amount
FROM table1
WHERE start_date BETWEEN CASE
WHEN extract(day from sysdate) <= 20
THEN trunc(sysdate -interval '1' month, 'month')
ELSE trunc(sysdate, 'month')
END
AND CASE
WHEN extract(day from sysdate) <= 20
THEN last_day(sysdate -interval '1' month)
ELSE last_day(sysdate)
END

ORA-01839: date not valid for month specified, How to avoid leap year issues

I have a table that stores a valid "custom date" for each person. That date may legitimately be Feb 29 of a leap year. The thing is in my SQL for my report I need to pull the "last" mm/dd for from this custom date field for that employee as of the most recent year. This date is never in the future so I can compare the year in the date and if its the current year pull it as is. However if it's any previous year (can go back as far as 1900) I need to instead pull that MM/DD and as of last year. So you can see how this will be a problem since there is no Feb-29-2018.
I'm looking for the best solution on how to handle this. I need it in my select portion and my conditional block where this date I'm trying to derive has to be >= the applydtm date. Here is a sample query to replicate the error. Simply switching the Feb-29 date to Feb-28 will show it working since it doesn't encounter that invalid leap year date. I would be okay with all Feb 29 dates 'converting' to Mar 1st if need be as long as the code is efficient as possible. DB = Oracle 11g
WITH sampledata (actualcustomdtm, textfield, applydtm) AS (
SELECT
TO_DATE('02/29/2012 00:00:00','mm/dd/yyyy hh24:mi:ss'),
'Leap Year',
sysdate
FROM dual
UNION
SELECT
TO_DATE('01/15/2019 00:00:00','mm/dd/yyyy hh24:mi:ss'),
'Non-Leap Year',
sysdate
FROM dual
)
SELECT
actualcustomdtm,
CASE WHEN TO_CHAR(actualcustomdtm, 'YYYY') = TO_CHAR(SYSDATE, 'YYYY')
THEN TO_CHAR(actualcustomdtm, 'MM/DD/YYYY')
ELSE TO_CHAR(actualcustomdtm, 'MM/DD')||'/'||TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -12), 'YYYY')
END AS "LASTACTCSTMDATE",
textfield,
applydtm
FROM sampledata
WHERE applydtm >=
TO_DATE(CASE WHEN TO_CHAR(actualcustomdtm, 'YYYY') = TO_CHAR(SYSDATE, 'YYYY')
THEN TO_CHAR(actualcustomdtm, 'MM/DD/YYYY')
ELSE TO_CHAR(actualcustomdtm, 'MM/DD')||'/'||TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -12), 'YYYY')
END, 'MM/DD/YYYY')
I believe this logic does what you want -- getting the most recent anniversary up to or including today's date:
select dte, add_months(dte, 12),
(case when to_char(dte, 'MMDD') > to_char(sysdate, 'MMDD')
then add_months(dte, 12 * (extract(year from sysdate) - extract(year from dte) - 1))
else add_months(dte, 12 * (extract(year from sysdate) - extract(year from dte)))
end)
from (select date '2016-02-29' as dte from dual union all
select date '2018-01-03' from dual
) x;

Current status based on week number ORACLE

I got this query on order to get all the days from the first day of the year (01/01/2018) to the end of next year (31/12/2019).
SELECT MYDATE,
TO_CHAR(NR_OF_SUNDAYS + 1,'FM09') WEEK_NUM,
FROM
(
SELECT MYDATE,
( (TRUNC(MYDATE,'DAY') - TRUNC(TRUNC(MYDATE,'YYYY'),'DAY')) / 7 ) +
CASE WHEN TO_CHAR(TRUNC(MYDATE,'YYYY'),'DAY') = 'SUN' THEN 1 ELSE 0 END AS NR_OF_SUNDAYS
FROM
( SELECT TRUNC (SYSDATE, 'YY') - 1 + LEVEL AS MYDATE
FROM DUAL
CONNECT BY LEVEL <= TRUNC (ADD_MONTHS (SYSDATE, 24), 'YY') -
TRUNC (SYSDATE, 'YY')
)
)
I need a column that specifies the following cases:
1) CASE WHEN MYDATE < TO_CHAR(SYSDATE, 'DD/MM/YYYY') THEN 'PAST DUE'
(this works its easy and no problem)
2) if my current =< mydate
week_num then 'CURRENT WEEK'(Excluding PAST DUE)
3) if my current week + one week then
'NEXT WEEK' (Excluding PAST DUE)
4) else FUTURE
Thanks a lot for your help.
So, in my answer I tried retain the logic behind your week number calculation.
However keep in mind that you could calculate week number using oracle to_char(date,'WW'), to_char(date,'IW'), to_char(date,'W') functions and then your life would be easier.
WW Week of year (1-53) where week 1 starts on the first day of the year and continues to the seventh day of the year.
W Week of month (1-5) where week 1 starts on the first day of the month and ends on the seventh.
IW Week of year (1-52 or 1-53) based on the ISO standard.
Having said all that here is my solution that uses only sql (note that defining and using a function would be a lot easier), based on your calculation method.
with date_table as (
SELECT MYDATE, to_number(TO_CHAR(NR_OF_SUNDAYS + 1,'FM09')) WEEK_NUM, to_number(to_char(MYDATE+1,'IW')) as nu
FROM
(
SELECT MYDATE,
( (TRUNC(MYDATE,'DAY') - TRUNC(TRUNC(MYDATE,'YYYY'),'DAY')) / 7 ) +
CASE WHEN TO_CHAR(TRUNC(MYDATE,'YYYY'),'DY', 'NLS_DATE_LANGUAGE = american') = 'SUN' THEN 1 ELSE 0 END AS NR_OF_SUNDAYS
FROM
( SELECT TRUNC (SYSDATE, 'YY') - 1 + LEVEL AS MYDATE
FROM DUAL
CONNECT BY LEVEL <= TRUNC (ADD_MONTHS (SYSDATE, 24), 'YY') -TRUNC (SYSDATE,'YY')
)
)
),
todays_week as
(
select distinct WEEK_NUM from date_table
where trunc(sysdate)=trunc(mydate)
),
pre_final as (
select MYDATE,WEEK_NUM, (select WEEK_NUM from todays_week) as todaysweek from date_table)
select MYDATE,sysdate,WEEK_NUM,todaysweek,
case when trunc(MYDATE) < trunc(sysdate) then 'PAST DUE'
when todaysweek = WEEK_NUM and abs(MYDATE-sysdate)<=7 then 'CURRENT WEEK'
when todaysweek +1 = WEEK_NUM and abs(MYDATE-sysdate)<=14 then 'Next Week'
else 'Future' end as description
from pre_final;
The main idea is to find today's week number and then use case when.
Here is my fiddle link with the results.
http://sqlfiddle.com/#!4/3149e4/148
EDIT 1:
Now, similar results one could achive with something like this:
select res.*,
case when trunc(MYDATE) < trunc(sysdate) then 'PAST DUE'
when todaysweek = WEEK_NUM and abs(MYDATE-sysdate)<=7 then 'CURRENT WEEK'
when todaysweek +1 = WEEK_NUM and abs(MYDATE-sysdate)<=14 then 'Next Week'
else 'Future' end as description
from (
SELECT MYDATE, to_number(to_char(MYDATE,'IW')) as WEEK_NUM,to_number(to_char(sysdate,'IW')) as todaysweek
FROM
( SELECT TRUNC (SYSDATE, 'YY') - 1 + LEVEL AS MYDATE
FROM DUAL
CONNECT BY LEVEL <= TRUNC (ADD_MONTHS (SYSDATE, 24), 'YY') -TRUNC (SYSDATE,'YY')
)) res

Returning a Date Range based on Day of the Month

Select *
From Orders
WHERE (
extract(day from sysdate)<=21
and
to_date(SCHEDULEDATE , 'yyyy/mm/dd') between
to_date((to_char(sysdate, 'YYYY')||'/'||cast((extract(month from sysdate)-1)as char)||'/22'),'yyyy/mm/dd') and to_date((to_char(sysdate,'YYYY')||'/'||cast((extract(month from sysdate))as char)||'/21'),'yyyy/mm/dd')
)
or
(
extract(day from sysdate)>21
and
to_date(SCHEDULEDATE , 'yyyy/mm/dd') between
to_date((to_char(sysdate, 'YYYY')||'/'||cast((extract(month from sysdate))as char)||'/22'),'yyyy/mm/dd') and to_date((to_char(sysdate, 'YYYY')||'/'||cast((extract(month from sysdate)+1)as char)||'/21'),'yyyy/mm/dd')
)
I'm trying to figure out a simple way of returning a set of date ranges based on the day of the Month. If the Day of the month of less than or Equal to I want it to return all orders the have a schedule date between the 22 of the Month before and the 21st of the Current month. If the Day of the month is greater than 21 I would like it return all orders that have a schedule date of the current month up to the end of the month. I've tried to use a case in the where with no luck. What I have now doesn't seem to work either. Any help would be appreciated.
I think this does what you want:
WHERE (extract(day from sysdate) <= 21 and
scheduledate >= add_months(trunc(sysdate, 'MON'), -1) + 21 and
scheduledate < trunc(sysdate, 'MON') + 21
) or
(extract(day from sysdate) > 21 and
trunc(scheduledate, 'MON') = trunc(sysdate, 'MON')
)