Calculate week before - sql

I have a table that is populated with info once a week, with the date when it is populated as a single column in the table.
I am working on a query that basically compares data from this week against the data from last week exclusively, therefore I need to know which week was last week. This is easy to accomplish in the following way:
WHERE TO_CHAR(LASTWEEK.DATE_GENERATED, 'IW') = TO_CHAR(SYSDATE, 'IW') -1
AND TO_CHAR(LASTWEEK.DATE_GENERATED, 'YY') = TO_CHAR(SYSDATE, 'YY')
The table may contain data from several years, therefore I also check that the year be the same.
The problem comes when we are in the first week of the year and we want to compare against the previous week. This way we are in week 1 minus 1 it gives us 0, but I want it to compare against week 54 from LAST year.
Is there a way to accomplish this?
I feel that the TRUNC function might work with this but I am unsure as to how to use it in this scenario, also keeping in mind that it should not compare against the same year but last year.
Thanks in advance!

I'd avoid using TO_CHAR in this event altogether ... stay within DATE format ... safer, especially when breaching years ..
where trunc(LASTWEEK.DATE_GENERATED,'IW') = trunc(sysdate-7,'IW')
That single condition should give it to you :)
You can see what it's doing here:
1 with w_data as (select sysdate-level d from dual connect by level <= 20 )
2 select d, trunc(d, 'IW') w
3* from w_data
SQL> /
D W
-------------------- --------------------
09-feb-2016 13:20:41 08-feb-2016 00:00:00
08-feb-2016 13:20:41 08-feb-2016 00:00:00
07-feb-2016 13:20:41 01-feb-2016 00:00:00
06-feb-2016 13:20:41 01-feb-2016 00:00:00
05-feb-2016 13:20:41 01-feb-2016 00:00:00
04-feb-2016 13:20:41 01-feb-2016 00:00:00
03-feb-2016 13:20:41 01-feb-2016 00:00:00
02-feb-2016 13:20:41 01-feb-2016 00:00:00
01-feb-2016 13:20:41 01-feb-2016 00:00:00
31-jan-2016 13:20:41 25-jan-2016 00:00:00
30-jan-2016 13:20:41 25-jan-2016 00:00:00
29-jan-2016 13:20:41 25-jan-2016 00:00:00
28-jan-2016 13:20:41 25-jan-2016 00:00:00
27-jan-2016 13:20:41 25-jan-2016 00:00:00
26-jan-2016 13:20:41 25-jan-2016 00:00:00
25-jan-2016 13:20:41 25-jan-2016 00:00:00
24-jan-2016 13:20:41 18-jan-2016 00:00:00
23-jan-2016 13:20:41 18-jan-2016 00:00:00
22-jan-2016 13:20:41 18-jan-2016 00:00:00
21-jan-2016 13:20:41 18-jan-2016 00:00:00
20 rows selected.

You can compare with a date a week ago, by using sysdate - 7, and combine with a single value:
WHERE TO_CHAR(LASTWEEK.DATE_GENERATED, 'IYYYIW') = TO_CHAR(SYSDATE - 7, 'IYYYIW')
Notice I'm using the IYYY year model to match the IW one, otherwise you'd get odd results.
But as #ditto shows it's cleaner to use TRUNC with the IW format mask. And as #MaxU pointed out truncating the source column will prevent indexes being used, unless you have a matching function-based index. You can avoid that by modifying Ditto's approach slightly:
WHERE LASTWEEK.DATE_GENERATED >= TRUNC(SYSDATE - 7, 'IW')
AND LASTWEEK.DATE_GENERATED < TRUNC(SYSDATE, 'IW');
As a demo:
with lastweek(date_generated) as (
select sysdate - level from dual connect by level <= 50
)
SELECT LASTWEEK.DATE_GENERATED
FROM LASTWEEK
WHERE LASTWEEK.DATE_GENERATED >= TRUNC(SYSDATE - 7, 'IW')
AND LASTWEEK.DATE_GENERATED < TRUNC(SYSDATE, 'IW');
DATE_GENERATED
--------------
2016-02-07
2016-02-06
2016-02-05
2016-02-04
2016-02-03
2016-02-02
2016-02-01
Or to see the year change, lets go back further, again just as a demo:
with lastweek(date_generated) as (
select sysdate - level from dual connect by level <= 50
)
SELECT LASTWEEK.DATE_GENERATED
FROM LASTWEEK
WHERE LASTWEEK.DATE_GENERATED >= TRUNC(SYSDATE - 42, 'IW')
AND LASTWEEK.DATE_GENERATED < TRUNC(SYSDATE - 35, 'IW');
DATE_GENERATED
--------------
2016-01-03
2016-01-02
2016-01-01
2015-12-31
2015-12-30
2015-12-29
2015-12-28

the query from Ditto will work, but it will not use indexes on "LASTWEEK.DATE_GENERATED" column, because of applied function (trunc), so i would go for the following:
select
next_day(to_date('2016-01-10','yyyy-mm-dd')-14, 'MONDAY'),
next_day(to_date('2016-01-10','yyyy-mm-dd')-14, 'SUNDAY')
from dual;
Output:
12/28/2015 01/03/2016
So your query would be:
WHERE LASTWEEK.DATE_GENERATED
between next_day(to_date('2016-01-10','yyyy-mm-dd')-14, 'MONDAY')
and next_day(to_date('2016-01-10','yyyy-mm-dd')-14, 'SUNDAY')

Related

Extract 5th Business Date from sysdate

I'm looking to extract the 5th Business Date data from database. Looking for pure 5th Business Date, no other business requirement like Holiday or New Year day.
Looking to extract 07/03/2022 from dual table using Oracle PL/SQL
Date
Day
Requirement
1/03/2022
Tuesday
1BD
2/03/2022
Wednesday
2BD
3/03/2022
Thursday
3BD
4/03/2022
Friday
4BD
5/03/2022
Saturday
Weekend
6/03/2022
Sunday
Weekend
7/03/2022
Monday
5BD
8/03/2022
Tuesday
6BD
9/03/2022
Wednesday
7BD
This is how I understood it.
Today is Thursday, 24.03.2022. It means that 5th business day looking backwards is Friday, 18.03.2022.
SQL> with test (datum, day) as
2 -- calendar
3 (select
4 trunc(sysdate) - &&par_number_of_days * 2 + level - 1,
5 to_char(trunc(sysdate) - &&par_number_of_days * 2 + level - 1, 'dy',
6 'nls_date_language = english')
7 from dual
8 connect by level <= (&&par_number_of_days * 2) + 1
9 ),
10 only_working_days as
11 -- remove weekends
12 (select datum,
13 day,
14 row_number() over (order by datum desc) rn
15 from test
16 where day not in ('sat', 'sun')
17 )
18 select datum, day, rn
19 from only_working_days
20 where rn = &&par_number_of_days;
Enter value for par_number_of_days: 5
DATUM DAY RN
---------- --- ----------
18.03.2022 fri 5
Or, 13th business day backwards is 08.03.2022:
SQL> undefine par_number_of_days
SQL> /
Enter value for par_number_of_days: 13
DATUM DAY RN
---------- --- ----------
08.03.2022 tue 13
SQL>
If it is, on the other hand, related to period since 1st of current, month, then
SQL> with test (datum, day) as
2 (select trunc(sysdate, 'mm') + level - 1,
3 to_char(trunc(sysdate, 'mm') + level - 1, 'dy', 'nls_date_language = english')
4 from dual
5 connect by level <= trunc(sysdate) - trunc(sysdate, 'mm') + 1
6 ),
7 only_working_days as
8 -- remove weekends
9 (select datum,
10 day,
11 row_number() over (order by datum) rn
12 from test
13 where day not in ('sat', 'sun')
14 )
15 select datum, day, rn
16 from only_working_days
17 where rn = &par_number_of_days;
Enter value for par_number_of_days: 5
DATUM DAY RN
---------- --- ----------
07.03.2022 mon 5
SQL> /
Enter value for par_number_of_days: 13
DATUM DAY RN
---------- --- ----------
17.03.2022 thu 13
SQL>
The 5th business day will always be 7 days ahead, since there will be 5 weekdays and 2 weekend days, so the simplest solution is:
SELECT TRUNC(SYSDATE) + INTERVAL '7' DAYS
FROM DUAL
More generally, if you want to add a number of business days to a date then you can calculate it using:
start_date
+ FLOOR(bd/5) * INTERVAL '7' DAY -- Full weeks
+ MOD(bd, 5) -- Part week
+ CASE
WHEN start_date - TRUNC(start_date, 'IW') + MOD(bd, 5) >= 5
THEN 2
WHEN start_date - TRUNC(start_date, 'IW') + MOD(bd, 5) < 0
THEN -2
ELSE 0
END -- Adjust for weekend
For example, given the sample data:
CREATE TABLE table_name (start_date, bd) AS
SELECT TRUNC(SYSDATE), LEVEL - 11 FROM DUAL CONNECT BY LEVEL <= 21
UNION ALL
SELECT DATE '2022-03-01', 5 FROM DUAL;
Then:
SELECT start_date,
bd,
start_date
+ FLOOR(bd/5) * INTERVAL '7' DAY -- Full weeks
+ MOD(bd, 5) -- Part week
+ CASE
WHEN start_date - TRUNC(start_date, 'IW') + MOD(bd, 5) >= 5
THEN 2
WHEN start_date - TRUNC(start_date, 'IW') + MOD(bd, 5) < 0
THEN -2
ELSE 0
END -- Adjust for weekend
AS adjusted_business_day
FROM table_name;
Outputs:
START_DATE
BD
ADJUSTED_BUSINESS_DAY
2022-03-24 00:00:00 (THU)
-10
2022-03-10 00:00:00 (THU)
2022-03-24 00:00:00 (THU)
-9
2022-03-04 00:00:00 (FRI)
2022-03-24 00:00:00 (THU)
-8
2022-03-07 00:00:00 (MON)
2022-03-24 00:00:00 (THU)
-7
2022-03-08 00:00:00 (TUE)
2022-03-24 00:00:00 (THU)
-6
2022-03-09 00:00:00 (WED)
2022-03-24 00:00:00 (THU)
-5
2022-03-17 00:00:00 (THU)
2022-03-24 00:00:00 (THU)
-4
2022-03-11 00:00:00 (FRI)
2022-03-24 00:00:00 (THU)
-3
2022-03-14 00:00:00 (MON)
2022-03-24 00:00:00 (THU)
-2
2022-03-15 00:00:00 (TUE)
2022-03-24 00:00:00 (THU)
-1
2022-03-16 00:00:00 (WED)
2022-03-24 00:00:00 (THU)
0
2022-03-24 00:00:00 (THU)
2022-03-24 00:00:00 (THU)
1
2022-03-25 00:00:00 (FRI)
2022-03-24 00:00:00 (THU)
2
2022-03-28 00:00:00 (MON)
2022-03-24 00:00:00 (THU)
3
2022-03-29 00:00:00 (TUE)
2022-03-24 00:00:00 (THU)
4
2022-03-30 00:00:00 (WED)
2022-03-24 00:00:00 (THU)
5
2022-03-31 00:00:00 (THU)
2022-03-24 00:00:00 (THU)
6
2022-04-01 00:00:00 (FRI)
2022-03-24 00:00:00 (THU)
7
2022-04-04 00:00:00 (MON)
2022-03-24 00:00:00 (THU)
8
2022-04-05 00:00:00 (TUE)
2022-03-24 00:00:00 (THU)
9
2022-04-06 00:00:00 (WED)
2022-03-24 00:00:00 (THU)
10
2022-04-07 00:00:00 (THU)
2022-03-01 00:00:00 (TUE)
5
2022-03-08 00:00:00 (TUE)
db<>fiddle here

Oracle get last weekday Mon-Fri

I would like to obtain the last weekday.
If it's Tues to Sat, it will be the previous day. If it's Sunday or Monday, it will be Friday.
So far, I've tried this, but I'm struggling to get the desired output.
SELECT
level AS dow,
trunc(sysdate, 'D') + level day,
to_char(trunc(sysdate, 'D') + level, 'Day') AS day_week,
CASE
WHEN to_char(trunc(sysdate, 'D') + level, 'Day') IN (
'Sunday',
'Monday'
) THEN
trunc(sysdate - 2, 'IW') + 4
ELSE
sysdate - 1
END calculation
FROM
dual
CONNECT BY
level <= 7;
This solution works independent of language and territory:
SELECT date_value,
date_value - CASE TRUNC(date_value) - TRUNC(date_value, 'IW')
WHEN 0 THEN 3 -- Monday
WHEN 6 THEN 2 -- Sunday
ELSE 1 -- Tuesday to Saturday
END AS previous_weekday
FROM table_name;
Which, for the sample data:
CREATE TABLE table_name (date_value) AS
SELECT TRUNC(sysdate - LEVEL + 1)
FROM DUAL
CONNECT BY LEVEL <= 7;
Outputs (with the date format YYYY-MM-DD HH24:MI:SS (DY)):
DATE_VALUE
PREVIOUS_WEEKDAY
2021-07-20 00:00:00 (TUE)
2021-07-19 00:00:00 (MON)
2021-07-19 00:00:00 (MON)
2021-07-16 00:00:00 (FRI)
2021-07-18 00:00:00 (SUN)
2021-07-16 00:00:00 (FRI)
2021-07-17 00:00:00 (SAT)
2021-07-16 00:00:00 (FRI)
2021-07-16 00:00:00 (FRI)
2021-07-15 00:00:00 (THU)
2021-07-15 00:00:00 (THU)
2021-07-14 00:00:00 (WED)
2021-07-14 00:00:00 (WED)
2021-07-13 00:00:00 (TUE)
db<>fiddle here

2020-02-29 minus one year caused error ORA-01839: date not valid for month specified

I have a simple query as:
select to_date('2020-02-29', 'yyyy-mm-dd') - interval '1' year from dual
I think the result should be 2019-02-28, but oracle throws error as:
Error report -
ORA-01839: date not valid for month specified
That is the documented behaviour; it even gives this as an example:
When interval calculations return a datetime value, the result must be
an actual datetime value or the database returns an error. For
example, the next two statements return errors:
SELECT TO_DATE('31-AUG-2004','DD-MON-YYYY') + TO_YMINTERVAL('0-1')
FROM DUAL;
SELECT TO_DATE('29-FEB-2004','DD-MON-YYYY') + TO_YMINTERVAL('1-0')
FROM DUAL;
The first fails because adding one month to a 31-day month would
result in September 31, which is not a valid date. The second fails
because adding one year to a date that exists only every four years is
not valid. However, the next statement succeeds, because adding four
years to a February 29 date is valid:
SELECT TO_DATE('29-FEB-2004', 'DD-MON-YYYY') + TO_YMINTERVAL('4-0')
FROM DUAL;
TO_DATE('
---------
29-FEB-08
The alternative is to use add_months(..., -12) (docs), which won't error:
select add_months(date '2020-02-29', -12) from dual;
ADD_MONTHS
----------
2019-02-28
But note how that deals with different number of days in the month; not really an issue when you're going back exactly a year, but still something to be aware of:
If date is the last day of the month or if the resulting month has fewer days than the day component of date, then the result is the last day of the resulting month.
So some of these might not do what you expected:
with rcte (dt) as (
select last_day(date '2020-01-01')
from dual
union all
select last_day(trunc(dt, 'MM') + interval '1' month)
from rcte
where dt < date '2020-06-01'
)
select dt,
add_months(dt, -12) as minus12, add_months(dt, -3) as minus3, add_months(dt, -1) as minus1,
add_months(dt, 1) as plus1, add_months(dt, 3) as plus3, add_months(dt, 12) as plus12
from rcte
order by dt;
DT MINUS12 MINUS3 MINUS1 PLUS1 PLUS3 PLUS12
---------- ---------- ---------- ---------- ---------- ---------- ----------
2020-01-31 2019-01-31 2019-10-31 2019-12-31 2020-02-29 2020-04-30 2021-01-31
2020-02-29 2019-02-28 2019-11-30 2020-01-31 2020-03-31 2020-05-31 2021-02-28
2020-03-31 2019-03-31 2019-12-31 2020-02-29 2020-04-30 2020-06-30 2021-03-31
2020-04-30 2019-04-30 2020-01-31 2020-03-31 2020-05-31 2020-07-31 2021-04-30
2020-05-31 2019-05-31 2020-02-29 2020-04-30 2020-06-30 2020-08-31 2021-05-31
2020-06-30 2019-06-30 2020-03-31 2020-05-31 2020-07-31 2020-09-30 2021-06-30

Trying to convert a date field, it leads to errors while conversion

I am trying the below query and keep getting the error for ResCreation date, I used CAST thinking that Rescreation date might have some bad fields. Not sure what field is wrong.
SELECT TOP(5)
[PCode]
,[ADate]
,[DDate]
,[ResCreationDate]
,[CancelDate]
FROM
[ResDomain]
WHERE
CAST(ResCreationDate AS DATE) >= '2018-01-01'
The error that I am getting:
Msg 241, Level 16, State 1, Line 2
Conversion failed when converting date and/or time from character string.
That's the sample data output:
PCode ADate DDate ResCreationDate CancelDate
FL7112 2018-07-30 00:00:00 2018-08-03 00:00:00 2018-05-29 16:34:30 2018-07-25 12:35:00
MT2091 2018-05-03 00:00:00 2018-05-04 00:00:00 2018-05-02 21:58:27 NULL
2075 2018-03-31 00:00:00 2018-04-01 00:00:00 2018-04-01 00:18:40 NULL
2012 2018-07-07 00:00:00 2018-07-08 00:00:00 2018-06-16 17:38:29 2018-06-30 03:20:00
2587 2018-06-30 00:00:00 2018-07-01 00:00:00 2018-06-30 11:18:10 NULL
Not sure if I should try another datatype, I used '2018-01-01 00:00:00' too in the where clause, but didn't work.
Use try_cast():
SELECT TOP (5) [PCode], [ADate], [DDate], [ResCreationDate]
FROM [ResDomain]
WHERE TRY_CAST(ResCreationDate as Date) >= '2018-01-01';
Or, better yet, find the bad data and fix it!
SELECT ResCreationDate
FROM ResDomain
WHERE TRY_CONVERT(Date, ResCreationDate) IS NULL;

Casting DATE returns "Invalid Month" error

My fiscal year starts on May 1 and ends Apr 30. I am trying to return the fiscal year start date using a CASE statement.
I.e.
CAST(
CASE
WHEN TO_NUMBER (TO_CHAR (GET_DATE, 'MM')) IN (11,
12,
5,
6,
7,
8,
9,
10)
THEN
'05/01/'
|| TO_NUMBER (TO_CHAR (TRUNC (get_date, 'year'), 'YYYY'))
WHEN TO_NUMBER (TO_CHAR (GET_DATE, 'MM')) IN (1,
2,
3,
4)
THEN
'05/01/'
|| TO_NUMBER (TO_CHAR (TRUNC (get_date, 'year'), 'YYYY') - 1)
END AS DATE)
I am getting "invalid month" when I use the cast but when I take it off, it defaults to number. Either way, i'm not getting the results I want.
Just subtract 4 (four) months off your date and pull the year out of it (use trunc to reduce it to the year) then add 4 (four) months back on
SELECT
ADD_MONTHS(
TRUNC(
ADD_MONTHS( <yourdate> ,-4),
'YEAR'),
4)
FROM DUAL;
As to why it works:
We have some example dates: 25-04-2009 13-07-2009
These are in the fiscal years beginning: 01-05-2008 01-05-2009
We subtract 4 months from the date: 25-12-2008 13-03-2009
We trunc down to the year start: 01-01-2008 01-01-2009
We add 4 months back on to get to May: 01-05-2009 01-05-2009
Why is it good/better than converting to string and back? Well, that's the reason right there. Dates are represented as a number, and this method keeps it as a number and works entirely off maths; adding, rounding and substracting. It's always better to avoid unnecessary data type conversions, because thy're slow, resource intensive and can introduce unexpected conversion errors
Using TRUNC with dates is one of the coolest things oracle does with dates that other databases just can't handle. Being able to take any date and TRUNC() it to the start of the year/month/day/hour/minute/weekday etc month is a great help in reports where events are logged to millisecond precision, but you want to summarise or work with them in terms of "number of things that happened this week/month" etc
For Oracle, this get the Start of Fiscal Year for any date.
Just replace "sysdate" function with a variable of type DATE or a column name of type DATE:
select /* for Oracle */
to_date(CASE
WHEN extract(month from sysdate)<5 then
extract(year from sysdate)-1
ELSE
extract(year from sysdate)
end||'-05-01',
'yyyy-mm-dd') as start_fiscal_year
from dual;
Testing on Oracle database using anonymous block:
Declare
/*=========================================================================================
-- objective: calculate Start fiscal date
-- Fisacal year starts on May 1 and ends April 30
--
-- https://stackoverflow.com/questions/52426117/casting-date-returns-invalid-month-error-in-plsql
--
-- Database: Oracle
--
-- 2018-09-20 alvalongo
==========================================================================================*/
dtStart_date date:=to_date('2018-01-10','yyyy-mm-dd');
dtAny_date date;
dtStart_fiscal_date date;
Begin
dbms_output.put_line('I |ANY_DATE |START_FISCAL_YEAR');
for nuI in 0..24 loop
dtAny_date:=add_months(dtStart_date,nuI);
--
select to_date(CASE
WHEN extract(month from dtAny_date)<5 then
extract(year from dtAny_date)-1
ELSE
extract(year from dtAny_date)
end||'-05-01','yyyy-mm-dd') as start_fiscal_year
into dtStart_fiscal_date
from dual;
if extract(month from dtAny_date)=5 then
dbms_output.put_line('--|----------|----------');
end if;
dbms_output.put_line(lpad(nuI,2)
||'|'||to_char(dtAny_date ,'yyyy-mm-dd')
||'|'||to_char(dtStart_fiscal_date,'yyyy-mm-dd')
);
End loop;
End;
/
Output using dbms_output buffer:
I |ANY_DATE |START_FISCAL_YEAR
0|2018-01-10|2017-05-01
1|2018-02-10|2017-05-01
2|2018-03-10|2017-05-01
3|2018-04-10|2017-05-01
--|----------|----------
4|2018-05-10|2018-05-01
5|2018-06-10|2018-05-01
6|2018-07-10|2018-05-01
7|2018-08-10|2018-05-01
8|2018-09-10|2018-05-01
9|2018-10-10|2018-05-01
10|2018-11-10|2018-05-01
11|2018-12-10|2018-05-01
12|2019-01-10|2018-05-01
13|2019-02-10|2018-05-01
14|2019-03-10|2018-05-01
15|2019-04-10|2018-05-01
--|----------|----------
16|2019-05-10|2019-05-01
17|2019-06-10|2019-05-01
18|2019-07-10|2019-05-01
19|2019-08-10|2019-05-01
20|2019-09-10|2019-05-01
21|2019-10-10|2019-05-01
22|2019-11-10|2019-05-01
23|2019-12-10|2019-05-01
24|2020-01-10|2019-05-01
Total execution time 517 ms
Expanding on #CaiusJard's answer, you can get the start of the fiscal year with:
add_months(trunc(add_months(get_date, -4), 'YYYY'), 4)
The add_months(get_date, -4) subtracts four months from the starting date, so a value in January to April will be adjusted to a date in September to December of the previous year. So for instance, 2018-03-11 will become 2017-11-11. But dates dates from May onwards will stay in the same year, so for instance 2018-07-04 becomes 2018-03-04.
Then the trunc(..., 'YYYY') truncates that adjusted value to the first day of its year. So 2018-03-11 becomes 2017-11-11 which becomes 2017-01-01; and 2018-07-04 becomes 2018-03-04 which becomes 2018-01-01.
Then the outer add_months(..., 4) adds four months back on to that adjusted value. So 2018-03-11 becomes 2017-11-11 which becomes 2017-01-01 which finally becomes 2017-05-01; and 2018-07-04 becomes 2018-03-04 which becomes 2018-01-01 which finally becomes 2018-05-01.
To get the last day of the fiscal year you can do the same thing but add an extra 12 months in the final calculation - which gives you the start of the next fiscal year - and then subtract one day:
add_months(trunc(add_months(get_date, -4), 'YYYY'), 16) - 1
Read more about the add_months() and trunc() functions, and about date arithmetic, in the documentation.
Demo with dummy dates in a CTE to show the steps in the adjustment:
with your_table (get_date) as (
select add_months(date '2018-01-15', level)
from dual
connect by level <= 30
)
select get_date,
add_months(get_date, -4) as adjusted_month,
trunc(add_months(get_date, -4), 'YYYY') as adjusted_year,
add_months(trunc(add_months(get_date, -4), 'YYYY'), 4) as start_date,
add_months(trunc(add_months(get_date, -4), 'YYYY'), 16) - 1 as end_date
from your_table
order by get_date;
GET_DATE ADJUSTED_M ADJUSTED_Y START_DATE END_DATE
---------- ---------- ---------- ---------- ----------
2018-02-15 2017-10-15 2017-01-01 2017-05-01 2018-04-30
2018-03-15 2017-11-15 2017-01-01 2017-05-01 2018-04-30
2018-04-15 2017-12-15 2017-01-01 2017-05-01 2018-04-30
2018-05-15 2018-01-15 2018-01-01 2018-05-01 2019-04-30
2018-06-15 2018-02-15 2018-01-01 2018-05-01 2019-04-30
2018-07-15 2018-03-15 2018-01-01 2018-05-01 2019-04-30
...
2019-01-15 2018-09-15 2018-01-01 2018-05-01 2019-04-30
2019-02-15 2018-10-15 2018-01-01 2018-05-01 2019-04-30
2019-03-15 2018-11-15 2018-01-01 2018-05-01 2019-04-30
...
2020-02-15 2019-10-15 2019-01-01 2019-05-01 2020-04-30
2020-03-15 2019-11-15 2019-01-01 2019-05-01 2020-04-30
2020-04-15 2019-12-15 2019-01-01 2019-05-01 2020-04-30
2020-05-15 2020-01-15 2020-01-01 2020-05-01 2021-04-30
2020-06-15 2020-02-15 2020-01-01 2020-05-01 2021-04-30
2020-07-15 2020-03-15 2020-01-01 2020-05-01 2021-04-30
db<>fiddle
As noted elsewhere in comments, your original code is erroring because cast(<string> as date) uses your session's NLS settings, and the string you are constructing does not match that setting. You could use to_date() instead of case so you can supply the expected format mask (see #alvalongo's answer!).
This should be work, if you want to maintain the CASE statement
select CASE WHEN
MONTH(CURRENT_DATE) < 5 THEN
YEAR(CURRENT_DATE)-1 ELSE YEAR(CURRENT_DATE)
end
I tried the following code and it worked for me. DATE is an inbuilt key word so we cannot use that as the column alias. I changed that to FS_START_DATE.
SELECT SYSDATE, TO_DATE(CASE
WHEN TO_NUMBER (TO_CHAR (SYSDATE, 'MM')) IN (11, 12, 5,6,7, 8,9, 10)
THEN '05/01/' || TO_NUMBER (TO_CHAR (TRUNC (SYSDATE, 'year'), 'YYYY'))
WHEN TO_NUMBER (TO_CHAR (SYSDATE, 'MM')) IN (1, 2, 3,4)
THEN '05/01/' || TO_NUMBER (TO_CHAR (TRUNC (SYSDATE, 'year'), 'YYYY') - 1)
END,'MM/DD/YYYY') AS FS_START_DATE FROM DUAL;
I am using TO_DATE function and this returns me 01-MAY-2018.