Oracle DBA Scheduler Flow - sql

I have a requirement to schedule the procedure using Oracle DBA Scheduler to run on the 4th working day every month (excluding bank holidays and weekends). After doing some research it seems that Oracle DBA Scheduler does not recognize working days or bank holidays.
I have come up with below two ideas:
schedule another procedure to run on the 1st of every month and check when is the 4th working day and adjust the repeat_interval in the first scheduled job so it runs on 4th working day.
schedule the job to run monthly but only for the first 7 days, and in the run procedure create a check if today is 4th working day and if yes then execute if not do not run.
I do not have much experience with scheduling jobs, so I am not sure if option 1 is recommended, but it seems easier. I guess option 2 is still ok, but it will be more time-consuming to complete.
What option would you use? Or maybe something completely different?
Thanks

You can have an SQL resulting with 4th working day in every month of the year. You should get holiday dates for the actual year.
Example:
Czech Republic Public Holidays 2022
Date
Day
Holiday
01.01.2022
Sat
New Year's Day
15.04.2022
Fri
Good Friday
18.04.2022
Mon
Easter Monday
01.05.2022
Sun
May Day
08.05.2022
Sun
Liberation Day
05.07.2022
Tue
St Cyril and St Methodius Day
06.07.2022
Wed
Jan Hus Day
28.09.2022
Wed
Statehood Day
28.10.2022
Fri
Independence Day
17.11.2022
Thu
Freedom and Democracy Day
24.12.2022
Sat
Christmas Eve
25.12.2022
Sun
Christmas Day
26.12.2022
Mon
2nd Day of Christmas
Create a CTE holidays and another CTE named days holding 365 days with some attributes like day of week, is it a working day or not and distinct counters for working and non working days within a month:
WITH
holidays AS
(
Select To_Date('01.01.2022', 'dd.mm.yyyy') "A_DATE", 'New Year''s Day' "A_NAME" From Dual Union All
Select To_Date('15.04.2022', 'dd.mm.yyyy') "A_DATE", 'Good Friday ' "A_NAME" From Dual Union All
Select To_Date('18.04.2022', 'dd.mm.yyyy') "A_DATE", 'Easter Monday' "A_NAME" From Dual Union All
Select To_Date('01.05.2022', 'dd.mm.yyyy') "A_DATE", 'May Day ' "A_NAME" From Dual Union All
Select To_Date('08.05.2022', 'dd.mm.yyyy') "A_DATE", 'Liberation Day' "A_NAME" From Dual Union All
Select To_Date('05.07.2022', 'dd.mm.yyyy') "A_DATE", 'St Cyril and St Methodius Day' "A_NAME" From Dual Union All
Select To_Date('06.07.2022', 'dd.mm.yyyy') "A_DATE", 'Jan Hus Day' "A_NAME" From Dual Union All
Select To_Date('28.09.2022', 'dd.mm.yyyy') "A_DATE", 'Statehood Day' "A_NAME" From Dual Union All
Select To_Date('28.10.2022', 'dd.mm.yyyy') "A_DATE", 'Independence Day' "A_NAME" From Dual Union All
Select To_Date('17.11.2022', 'dd.mm.yyyy') "A_DATE", 'Freedom and Democracy Day' "A_NAME" From Dual Union All
Select To_Date('24.12.2022', 'dd.mm.yyyy') "A_DATE", 'Christmas Eve ' "A_NAME" From Dual Union All
Select To_Date('25.12.2022', 'dd.mm.yyyy') "A_DATE", 'Christmas Day' "A_NAME" From Dual Union All
Select To_Date('26.12.2022', 'dd.mm.yyyy') "A_DATE", '2nd Day of Christmas ' "A_NAME" From Dual
),
days AS
( Select
To_Date('01.01.' || To_Char(SYSDATE, 'yyyy'), 'dd.mm.yyyy') + (LEVEL - 1) "DATE_ID",
To_Char(To_Date('01.01.' || To_Char(SYSDATE, 'yyyy'), 'dd.mm.yyyy') + (LEVEL - 1), 'DY') "DAY_OF_WEEK",
CASE
WHEN To_Char(To_Date('01.01.' || To_Char(SYSDATE, 'yyyy'), 'dd.mm.yyyy') + (LEVEL - 1), 'DY') NOT IN('SAT', 'SUN') And
To_Date('01.01.' || To_Char(SYSDATE, 'yyyy'), 'dd.mm.yyyy') + (LEVEL - 1) Not IN(Select A_DATE From holidays)
THEN 'YES'
ELSE '-'
END "WRKDAY",
Count(*) OVER(Partition By To_Char(To_Date('01.01.' || To_Char(SYSDATE, 'yyyy'), 'dd.mm.yyyy') + (LEVEL - 1), 'yyyymm') ||
CASE
WHEN To_Char(To_Date('01.01.' || To_Char(SYSDATE, 'yyyy'), 'dd.mm.yyyy') + (LEVEL - 1), 'DY') NOT IN('SAT', 'SUN') And
To_Date('01.01.' || To_Char(SYSDATE, 'yyyy'), 'dd.mm.yyyy') + (LEVEL - 1) Not IN(Select A_DATE From holidays)
THEN 'YES'
ELSE '-'
END
Order By To_Date('01.01.' || To_Char(SYSDATE, 'yyyy'), 'dd.mm.yyyy') + (LEVEL - 1)) "DAY_NUM"
From
Dual
Connect By
LEVEL <= 365
Order By
LEVEL
)
Now you can get all the dates of interest for year 2022:
Select
DATE_ID, DAY_OF_WEEK, WRKDAY, DAY_NUM
From
days
Where
WRKDAY = 'YES' And DAY_NUM = 4
Order By
DATE_ID
The result is:
DATE_ID
DAY_OF_WEEK
WRKDAY
DAY_NUM
06-JAN-22
THU
YES
4
04-FEB-22
FRI
YES
4
04-MAR-22
FRI
YES
4
06-APR-22
WED
YES
4
05-MAY-22
THU
YES
4
06-JUN-22
MON
YES
4
08-JUL-22
FRI
YES
4
04-AUG-22
THU
YES
4
06-SEP-22
TUE
YES
4
06-OCT-22
THU
YES
4
04-NOV-22
FRI
YES
4
06-DEC-22
TUE
YES
4
This dataset now can be used for any scheduling policy that you want. You just have to check if your actual date is listed within or you can use these dates to instruct the scheduler.
Regards...

Related

Oracle SQL - Last 3 full months of data

I need to get the last 3 full months of data so March, April, and May in this case. This almost gives me what I want but it includes June 1st which I don't need. I need March 1st through May 31st.
Oracle SQL:
where d.D_DATE between add_months(trunc(sysdate, 'month'), -3) and add_months(trunc(sysdate, 'month'),0)
Use >= and < rather than BETWEEN:
SELECT *
FROM d
WHERE d.D_DATE >= ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -3)
AND d.D_DATE < TRUNC(SYSDATE, 'MM')
Which, for the sample data:
CREATE TABLE d (D_DATE) AS
SELECT ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -3) - INTERVAL '1' SECOND FROM DUAL UNION ALL
SELECT ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -3) FROM DUAL UNION ALL
SELECT TRUNC(SYSDATE, 'MM') - INTERVAL '1' DAY FROM DUAL UNION ALL
SELECT TRUNC(SYSDATE, 'MM') - INTERVAL '1' DAY FROM DUAL UNION ALL
SELECT TRUNC(SYSDATE, 'MM') - INTERVAL '1' DAY + INTERVAL '1' SECOND FROM DUAL UNION ALL
SELECT TRUNC(SYSDATE, 'MM') - INTERVAL '1' SECOND FROM DUAL UNION ALL
SELECT TRUNC(SYSDATE, 'MM') FROM DUAL;
Outputs:
D_DATE
2022-03-01 00:00:00
2022-05-31 00:00:00
2022-05-31 00:00:00
2022-05-31 00:00:01
2022-05-31 23:59:59
db<>fiddle here
Got it. where d.D_DATE between add_months(trunc(sysdate, 'month'), -3) and last_day(add_months(trunc(sysdate, 'month'),-1))

How to get previous working date using trunc(sysdate) in oracle

Greetings for the day!
i have written a python script that will run a select query using oracle database and will share the result with users based on the result it gets from that query. My aim is to run it through task scheduler on which it should automatically adjust the date mentioned in sql query and should always pick the last business day means if its monday, it should run the query with asof day as friday, if tuesday then asod day as Monday and so on.
Note : the report in the query runs on T+1 basis means if asof date is 21st Apr 2022 means it's actual start time would be 22 Apr 2022, so when it will run on 25th Apr (Monday) the asof date would be 22nd Apr
select* from snap_states
where asof = trunc(sysdate)-1
and upper(system) like ('LOANSL%')
order by start_time;***
If you're skipping weekends, then you could
SQL> with datum (sys_date) as
2 (select date '2022-04-23' from dual union all -- Saturday
3 select date '2022-04-24' from dual union all -- Sunday
4 select date '2022-04-25' from dual union all -- Monday
5 select date '2022-04-26' from dual -- Tuesday
6 )
7 select to_char(sys_date, 'dd.mm.yyyy, Dy') sys_date,
8 trunc(sys_date - case to_char(sys_date, 'Dy', 'nls_date_language = english')
9 when 'Sun' then 2
10 when 'Mon' then 3
11 else 1
12 end) as prev_work_day
13 from datum;
SYS_DATE PREV_WORK_
------------------------ ----------
23.04.2022, Sat 22.04.2022
24.04.2022, Sun 22.04.2022
25.04.2022, Mon 22.04.2022
26.04.2022, Tue 25.04.2022
SQL>
Applied to your query:
select *
from snap_states
where asof = trunc(sysdate - case to_char(sysdate, 'Dy', 'nls_date_language = english')
when 'Sun' then 2
when 'Mon' then 3
else 1
end)
and upper(system) like 'LOANSL%'
order by start_time;

How to call a dynamic day in sql?

I have a table I want to pull all date records before the most recent Friday. I know you can use sysdate (or getdate) to pull the current day, but all the solutions to similar questions I've looked at explicitly specify the numeric day of the week in the query. Todays Thursday so the below would work, but is there a dynamic alternative to this?
SELECT *
FROM table
WHERE datefield < sysdate - 6
You could use:
SELECT NEXT_DAY(TRUNC(SYSDATE) - 7, 'FRIDAY') AS last_friday
FROM DUAL;
However, if someone tries to query the data and is using a different language then you will get an error. I.e.:
ALTER SESSION SET NLS_DATE_LANGUAGE = 'FRENCH';
SELECT NEXT_DAY(TRUNC(SYSDATE) - 7, 'FRIDAY') AS last_friday
FROM DUAL;
Outputs:
ORA-01846: not a valid day of the week
A solution that works regardless of the language is to compare the day to the start of the ISO week (which is always a Monday):
SELECT TRUNC(SYSDATE, 'IW')
+ CASE WHEN SYSDATE - TRUNC(SYSDATE, 'IW') < 5
THEN -3
ELSE +4
END AS last_friday
FROM DUAL;
Outputs (with the NLS_DATE_FORMAT set to YYYY-MM-DD HH24:MI:SS (DY)):
LAST_FRIDAY
2022-01-14 00:00:00 (FRI)
db<>fiddle here
Your query would be:
SELECT *
FROM table
WHERE datefield < TRUNC(SYSDATE, 'IW')
+ CASE WHEN SYSDATE - TRUNC(SYSDATE, 'IW') < 5
THEN -3
ELSE +4
END
next_day function might help.
SQL> with test (datum) as
2 -- sample data; this January up to today
3 (select trunc(sysdate, 'mm') + level - 1
4 from dual
5 connect by level <= 20
6 )
7 select to_char(datum, 'dd.mm.yyyy, dy') datum
8 from test
9 where datum < next_day(sysdate - 7, 'FRIDAY')
10 order by datum;
DATUM
------------------------
01.01.2022, sat
02.01.2022, sun
03.01.2022, mon
04.01.2022, tue
05.01.2022, wed
06.01.2022, thu
07.01.2022, fri
08.01.2022, sat
09.01.2022, sun
10.01.2022, mon
11.01.2022, tue
12.01.2022, wed
13.01.2022, thu
14.01.2022, fri
14 rows selected.
SQL>

Oracle SQL get First Business Day date of given date

How can i get the date of the first business day of a given date .
For example:
01-AUG-21 is Sunday, so the first business day is 02-AUG-21 .
You can use a simple case statement in SQL or PL/SQL. In the example below, just replace SYSDATE + LEVEL with the date you would like to use.
SELECT SYSDATE + LEVEL AS some_date,
TO_CHAR (SYSDATE + LEVEL, 'DY') AS some_day_of_week,
SYSDATE
+ LEVEL
+ CASE TO_CHAR (SYSDATE + LEVEL, 'DY') WHEN 'SAT' THEN 2 WHEN 'SUN' THEN 1 ELSE 0 END AS business_day
FROM DUAL
CONNECT BY LEVEL <= 14;
SOME_DATE SOME_DAY_OF_WEEK BUSINESS_DAY
____________ ___________________ ________________
02-JUL-21 FRI 02-JUL-21
03-JUL-21 SAT 05-JUL-21
04-JUL-21 SUN 05-JUL-21
05-JUL-21 MON 05-JUL-21
06-JUL-21 TUE 06-JUL-21
07-JUL-21 WED 07-JUL-21
08-JUL-21 THU 08-JUL-21
09-JUL-21 FRI 09-JUL-21
10-JUL-21 SAT 12-JUL-21
11-JUL-21 SUN 12-JUL-21
12-JUL-21 MON 12-JUL-21
13-JUL-21 TUE 13-JUL-21
14-JUL-21 WED 14-JUL-21
15-JUL-21 THU 15-JUL-21
Here is a PL/SQL example of the same logic
DECLARE
FUNCTION get_business_day (p_date DATE)
RETURN DATE
IS
BEGIN
RETURN TRUNC (
p_date
+ CASE TO_CHAR (p_date, 'DY')
WHEN 'SAT' THEN 2
WHEN 'SUN' THEN 1
ELSE 0
END);
END;
BEGIN
DBMS_OUTPUT.put_line (get_business_day (p_date => SYSDATE));
END;
/
Here's one option: as it is only about 1 week, create a little one-week-calendar and fetch date which is larger than the one entered as a parameter, and which isn't a weekend.
with little_calendar as
(select to_date(:par_datum, 'dd.mm.yyyy') + level - 1 datum
from dual
connect by level <= 7
)
select min(datum)
from little_calendar
where datum > to_date(:par_datum, 'dd.mm.yyyy')
and to_char(datum, 'dy', 'nls_date_language = english')
not in ('sat', 'sun');
A few examples in SQL*Plus:
SQL> with little_calendar as
2 (select to_date('&&par_datum', 'dd.mm.yyyy') + level - 1 datum
3 from dual
4 connect by level <= 7
5 )
6 select min(datum)
7 from little_calendar
8 where datum > to_date('&&par_datum', 'dd.mm.yyyy')
9 and to_char(datum, 'dy', 'nls_date_language = english')
10 not in ('sat', 'sun');
Enter value for par_datum: 01.07.2021 --> the 1st working day after 01.07.2021 (Thursday) ...
MIN(DATUM)
---------------
02.07.2021, fri --> ... is 02.07.2021 (Friday)
SQL> undefine par_datum
SQL> /
Enter value for par_datum: 02.07.2021 --> working day that follows 02.07.2021 (Friday) ...
MIN(DATUM)
---------------
05.07.2021, mon --> ... is 05.07.2021 (Monday)
SQL>

Insert weekly dates starting fridays

I'm trying to insert weekly dates in my table, the start date is always on Fridays and the end date is always on thursday. I'm using this code :
CREATE TABLE WEEK AS
WITH generator AS (
SELECT DATE '2015-01-02' + LEVEL - 1 dt
FROM dual
CONNECT BY LEVEL <= DATE '2016-01-21' - DATE '2015-01-02' + 1
)
SELECT to_char(dt, 'YYYY "SEM"IW') "KEY",
dt "DATE_START",
least(next_day(dt - 1, to_char(DATE '2015-01-08', 'DAY')),
last_day(dt)) "DATE_END"
FROM generator
WHERE to_char(dt, 'D') = to_char(DATE '2015-01-02', 'D');
The code is working for weeks on the same month, but if I have a starting date on a month and the finish date on the next month, there's no data inserting in my table.
For example :
Date_ START | DATE_END
29-05-2015 | 31-05-2015
05-06-2015 | 11-05-2015
Instead of 31-05-2015 I should have 04-06-2015.
I think the following is what you're after:
with generator as (select date '2015-05-29' + (level - 1)*7 dt
from dual
connect by level <= (date '2016-01-21' - date '2015-05-29')/7 + 1)
select to_char(dt, 'YYYY "SEM"IW') "KEY",
dt "DATE_START",
dt + 6 "DATE_END"
from generator;
KEY DATE_START DATE_END
---------- ---------- ----------
2015 SEM22 2015-05-29 2015-06-04
2015 SEM23 2015-06-05 2015-06-11
2015 SEM24 2015-06-12 2015-06-18
2015 SEM25 2015-06-19 2015-06-25
<snip>
2016 SEM01 2016-01-08 2016-01-14
2016 SEM02 2016-01-15 2016-01-21
This is assuming that the dates you have specified in the generator subquery have already been determined to be a Friday. Otherwise you could use something like trunc(<date> - 4, 'iw') + 4 or trunc(<date> + 3, 'iw') + 4 (depending on whether you want the previous or next Friday to be included for the date specified) to make sure that the seed date is definitely a Friday.
Maybe just an alternative you can try analytical function here. But as
suggested by Boniest "just adding days" will be better approach. Have
fun
WITH generator AS
(SELECT DATE '2015-01-02' + LEVEL - 1 dt
FROM dual
CONNECT BY LEVEL <= DATE '2016-01-21' - DATE '2015-01-02' + 1
)
-- select * from generator;
SELECT TO_CHAR(dt, 'YYYY "SEM"IW') "KEY",
dt "DATE_START",
lead(dt) over (ORDER BY (dt)) -1 "End Date"
FROM generator
WHERE TO_CHAR(dt, 'D') = TO_CHAR(DATE '2015-01-02', 'D');
----------------------------------OUTPUT-----------------------------------------
**KEY DATE_START End Date**
2015 SEM18 05/01/2015 05/07/2015
2015 SEM19 05/08/2015 05/14/2015
2015 SEM20 05/15/2015 05/21/2015
2015 SEM21 05/22/2015 05/28/2015
2015 SEM22 05/29/2015 06/04/2015
----------------------------------------------------------------------------------