how to check a date is in current financial year - sql

I am trying to check a condition if given date is in current financial year i.e april to march. but not getting any idea how to do
code
select nvl(Count(1), 0)+1 from ASET where IPE='O' and irt in (SELECT EXTRACT (YEAR FROM ADD_MONTHS (SYSDATE, -3))
|| '-'
|| EXTRACT (YEAR FROM ADD_MONTHS (SYSDATE, 9))
FROM DUAL)
irt is date 01-09-2020.

I think the simplest approach is to offset the date column and sysdate by 3 months, and compare the year part:
where to_char(add_months(irt, -3), 'yyyy') = to_char(add_months(sysdate, -3), 'yyyy')

Related

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;

Causing a invalid day error when using the adhoc dates on the WHERE clause

I am told to create a calendar-like list of dates based on existing records and to tell if a record exists on that date.
To have a sample scenario, take these records from a sample table: TIME_LOG(ID, PUNCH_TIME).
1 1/1/2018 8:00:00
2 1/1/2018 12:12:00
...
n 2/14/2020 8:00:00
In this example, what I need to do is to:
Get all the months present on the TIME_LOG, which are January 2018 and February 2020.
List all dates inside those two months, which are January 1 - 31, 2018 plus February 1 - 29, 2020. And then set it as column DATE_TOKEN of the result set.
Set 'Record found' or 'No records found' whether the value from DATE_TOKEN column exists from TIME_LOG. Make that as column IS_FOUND of the result set.
To retrieve that result set, this is my preliminary query:
SELECT a.date_token,
NVL2 (b.date_token, 'Record found.',
'No records found.') AS is_found
FROM (SELECT TO_DATE (a.MONTH || '/' || b.DAY || '/' || a.YEAR,
'MM/DD/YYYY'
) AS date_token
FROM (SELECT TO_CHAR (EXTRACT (MONTH FROM a.punch_time)
) AS MONTH,
TO_CHAR (EXTRACT (YEAR FROM a.punch_time)) AS YEAR
FROM vw_each_punch a
GROUP BY TO_CHAR (EXTRACT (MONTH FROM a.punch_time)),
TO_CHAR (EXTRACT (YEAR FROM a.punch_time))) a
JOIN
(SELECT TO_CHAR (ROWNUM) AS DAY
FROM DUAL
CONNECT BY ROWNUM <= 31) b
-- I placed this condition to eliminate dates such as February 31, etc.
-- and it works unless I uncomment the WHERE clause below.
ON b.DAY <=
EXTRACT (DAY FROM LAST_DAY (TO_DATE ( a.MONTH
|| '/1/'
|| a.YEAR,
'MM/DD/YYYY'
)
)
)
) a
LEFT JOIN
(SELECT TRUNC (a.punch_time) AS date_token
FROM vw_each_punch a
GROUP BY TRUNC (a.punch_time)) b ON b.date_token = a.date_token
-- WHERE TRUNC (a.date_token, 'MONTH') = '1-FEB-2020'
ORDER BY 1, 2
This works fine and retrieves 60 rows on the resultset, similar to this (DATE_TOKEN, IS_FOUND):
1/1/2018 Record found.
1/2/2018 No records found.
1/3/2018 No records found.
...
2/13/2018 No records found.
2/14/2018 Record found.
2/15/2018 No records found.
...
until I needed to filter the resultset with certain months. When I try to uncomment the WHERE clause on my query to show only the dates on February 2020, it throws me an error:
ORA-01839: date not valid for month specified
The resultset loads completely without any errors as long as there are no WHERE clause, shown by the previous resultset with 60 rows. I suspect the error originates from the fact that February 2020 is less than 31 days, but that has been removed by b.DAY <= EXTRACT (DAY FROM LAST_DAY (TO_DATE (a.MONTH || '/1/' || a.YEAR, 'MM/DD/YYYY'))) condition. Also, I changed the condition from February 2020 to January 2018 but it still doesn't work.
Is there any way I can use the WHERE clause without it throwing an ORA-01839 error?
Here is a running SQL sample using subquery factoring if you need to run it ASAP:
WITH time_log_temp (id, punch_time)
AS (SELECT 1,
To_date('2018-1-1 8:00:00 AM', 'YYYY-MM-DD HH:MI:SS AM')
FROM dual
UNION ALL
SELECT 2,
To_date('2018-1-1 12:12:00 AM', 'YYYY-MM-DD HH:MI:SS AM')
FROM dual
UNION ALL
SELECT 2,
To_date('2020-2-14 8:00:00 AM', 'YYYY-MM-DD HH:MI:SS AM')
FROM dual),
inter
AS (SELECT a.date_token,
Nvl2 (b.date_token, 'Record found.', 'No records found.') AS
is_found
FROM (SELECT To_date (a.month
|| '/'
|| b.day
|| '/'
|| a.year, 'MM/DD/YYYY') AS date_token
FROM (SELECT To_char (Extract (month FROM a.punch_time)) AS
MONTH,
To_char (Extract (year FROM a.punch_time)) AS
YEAR
FROM time_log_temp a
GROUP BY To_char (Extract (month FROM a.punch_time)),
To_char (Extract (year FROM a.punch_time))) a
join (SELECT To_char (ROWNUM) AS DAY
FROM dual
CONNECT BY ROWNUM <= 31) b
-- I placed this condition to eliminate dates such as February 31, etc.
-- and it works unless I uncomment the WHERE clause below.
ON b.day <= Extract (day FROM Last_day (
To_date (a.month
|| '/1/'
|| a.year,
'MM/DD/YYYY')))) a
left join (SELECT Trunc (a.punch_time) AS date_token
FROM time_log_temp a
GROUP BY Trunc (a.punch_time)) b
ON b.date_token = a.date_token
ORDER BY 1,
2)
SELECT *
FROM inter
-- WHERE Trunc(date_token, 'MONTH') = '1-JAN-2018'
I'm not sure how you're getting exactly that error from the filters you've shown, but fundamentally the problem is that you're comparing your (truncated) date with a string, which has to be implicitly converted to a date for the comparison. That relies on your NLS settings, specifically NLS_DATE_FORMAT; and that clearly don't match the string format.
You should change the filter to compare against a date instead:
WHERE Trunc(date_token, 'MONTH') = to_date('1-FEB-2020', 'DD-MON-YYYY')
... which is still relying on your NLS_DATE_LANGUAGE setting for the month name, or slightly better:
WHERE Trunc(date_token, 'MONTH') = to_date('01-02-2020', 'DD-MM-YYYY')
or better still, shorter and unambiguous, use an ANSI date literal:
WHERE Trunc(date_token, 'MONTH') = DATE'2020-02-01'
Another approach that avoids having to truncate the date column (which may prevent an index being used in your real query) is to use a date range instead:
WHERE date_token >= DATE '2020-02-01' AND date_token < DATE'2020-03-01'

first date and last date of month from month and year

How to get first date and last date of month in oracle by giving input parameter as month.
For eg. if i give input month as 'Jan' and Year as '2016' it should give first date and last date of the month.
You can use TRUNC for that:
First day:
TRUNC(your_date, 'MM')
Last day:
ADD_MONTHS(TRUNC(your_date, 'MM'), 1) - 1
You basically TRUNC the month to its first day, add one month to get the first day of the next month, and then go back a day.
If you do not have a date in the month but only the month and the year you can simply use the first of each month to construct a date:
TO_DATE('1.' || your_month || '.' || your_year, 'DD.MM.YYYY')
ADD_MONTHS(TO_DATE('1.' || your_month || '.' || your_year, 'DD.MM.YYYY'), 1) - 1
You can use the TRUNC and LAST_DAY functions for this purpose.
select TRUNC(d,'MM'), LAST_DAY(d)
from (select to_date('01-2016','MM-YYYY') as d from dual);
01-JAN-2016 00:00:00 31-JAN-2016 00:00:00

Current Financial Year to sysdate

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;

Date conversion issue in sql

I would like to convert the payment day field from AUTO_TABLE into a day of the month. The payment day is stored in string format and the first nine days are stored as single character. I want to append a '0' string before the payment day for day 1-9 and convert the resulting string into 'DD'. The query worked for two months, but Oracle throws an error stating "invalid Month" when I attempt to convert the string into a date. How can I convert the payment day into two character decimals and proceed to concatenate day with the current month and year? Thanks for your help.
Select case when Payment_Day <> to_char(sysdate, 'dd')
then Payment_Day
end as Payment_day,
Payment_Day2,
trunc(sysdate) - 8 as DateEdit2,
trunc(sysdate) - 15 DateEdit1
From(
Select case when Payment_Day2 > trunc(sysdate)
then Payment_day2 - 31
else Payment_Day2 end as Payment_Day2,
Payment_Day, theSysdate as theSysdate
From(
Select distinct to_date(Payment_Day2, 'MM/DD/YYYY') as Payment_Day2,
Payment_Day, theSysdate
From(
Select thePIDM,
to_char(DateEdit, 'MM') || '/' || to_char(Payment_Day, '00') || '/' || to_char(sysdate, 'YYYY') as Payment_Day2,
to_char(Payment_Day) as Payment_Day, Trunc(theSysdate) theSysdate
From (
Select distinct PIDM as thePIDM,
to_char(Payment_Day) as Payment_Day,
trunc(sysdate) as DateEdit,
to_char(sysdate, 'DD') as theSysdate
from AUTO_TABLE
Group by PIDM, to_char(Payment_Day)
)
)
Order by Payment_Day2
)
Order by Payment_Day2
)
The query worked for two months. Yes you were lucky the run it in July and August, both having 31 days.
The problem is in the line
to_char(DateEdit, 'MM') || '/' || to_char(Payment_Day, '00') || '/' || to_char(sysdate, 'YYYY') as Payment_Day2,
which mix the payment_day with the current month (from sysdate). This leads to invalid dates such as 09/ 31/2015.
The remedy is in reducing the payment day to the last day of the current month
-- instead of
-- to_char(Payment_Day) as Payment_Day,
-- limit the payment day to the last day of the current month
to_char(least(to_number(Payment_Day),CAST(to_char(LAST_DAY(sysdate),'dd') AS INT))) as Payment_Day,