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
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;
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'
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
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;
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,