Reputation: 41
We use a materialized view with all days from 01/01/2010 to 31/12/2030. There is a date field associated with a flag is_working_day 0/1.
I need to get the previous business day of a day.
Example : we are the 01/01/n, previous business day is 31/12/n-1. We are 02/01/n, previous business day is also 31/12/n-1.
I have some test code here : https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=215d5e6aa6673f2273f3766f58093bc6
sample table :
DATE_D DAY_OF_WEEK_DESC_EN IS_WORKING_DAY
01-JAN-10 Friday 0
02-JAN-10 Saturday 0
03-JAN-10 Sunday 0
04-JAN-10 Monday 1
05-JAN-10 Tuesday 1
06-JAN-10 Wednesday 1
07-JAN-10 Thursday 1
08-JAN-10 Friday 1
09-JAN-10 Saturday 0
10-JAN-10 Sunday 0
select
date_d,
lag(date_d) over (order by date_d) as last_working_day,
day_of_week_desc_en
from oracle_calendar
where is_working_day = 1
Expected :
DATE_D LAST_WORKING_DAY DAY_OF_WEEK_DESC_EN IS_WORKING_DAY
01-JAN-10 Friday 0
02-JAN-10 Saturday 0
03-JAN-10 04-JAN-10 Sunday 0
04-JAN-10 04-JAN-10 Monday 1
05-JAN-10 04-JAN-10 Tuesday 1
06-JAN-10 05-JAN-10 Wednesday 1
07-JAN-10 06-JAN-10 Thursday 1
08-JAN-10 07-JAN-10 Friday 1
09-JAN-10 08-JAN-10 Saturday 0
10-JAN-10 08-JAN-10 Sunday 0
Basicly I use the function LAG() of Oracle to get the previous business day of a business day only, but I have to use the LEAD() function to get the last business day of a closed day. If there are consecutive closed days, I have to use this LEAD() function as many as there is consecutive closed days.
I think there is a better method to get all the previous business day of all consecutive closed days.
Upvotes: 0
Views: 137
Reputation: 168051
You can use LAG
with the IGNORE NULLS
clause and a CASE
statement:
Test Data:
CREATE TABLE oracle_calendar (
DATE_D DATE,
DAY_OF_WEEK_DESC_EN VARCHAR2(9) GENERATED ALWAYS AS ( CAST( RTRIM( TO_CHAR( DATE_D, 'Day' ) ) AS VARCHAR2(9) ) ),
IS_WORKING_DAY NUMBER(1,0)
);
INSERT INTO oracle_calendar( date_d, is_working_day )
SELECT DATE '2010-01-01', 0 FROM DUAL UNION ALL
SELECT DATE '2010-01-02', 0 FROM DUAL UNION ALL
SELECT DATE '2010-01-03', 0 FROM DUAL UNION ALL
SELECT DATE '2010-01-04', 1 FROM DUAL UNION ALL
SELECT DATE '2010-01-05', 1 FROM DUAL UNION ALL
SELECT DATE '2010-01-06', 1 FROM DUAL UNION ALL
SELECT DATE '2010-01-07', 1 FROM DUAL UNION ALL
SELECT DATE '2010-01-08', 1 FROM DUAL UNION ALL
SELECT DATE '2010-01-09', 0 FROM DUAL UNION ALL
SELECT DATE '2010-01-10', 0 FROM DUAL;
Query:
SELECT date_d,
LAG( CASE is_working_day WHEN 1 THEN date_d END, 1, NULL )
IGNORE NULLS OVER ( ORDER BY date_d) AS last_working_day,
day_of_week_desc_en,
is_working_day
FROM oracle_calendar
Output:
DATE_D | LAST_WORKING_DAY | DAY_OF_WEEK_DESC_EN | IS_WORKING_DAY :-------- | :--------------- | :------------------ | -------------: 01-JAN-10 | null | Friday | 0 02-JAN-10 | null | Saturday | 0 03-JAN-10 | null | Sunday | 0 04-JAN-10 | null | Monday | 1 05-JAN-10 | 04-JAN-10 | Tuesday | 1 06-JAN-10 | 05-JAN-10 | Wednesday | 1 07-JAN-10 | 06-JAN-10 | Thursday | 1 08-JAN-10 | 07-JAN-10 | Friday | 1 09-JAN-10 | 08-JAN-10 | Saturday | 0 10-JAN-10 | 08-JAN-10 | Sunday | 0
db<>fiddle here
Query 2:
To get rid of all the NULL
values at the start and use the first working day:
SELECT date_d,
COALESCE(
LAG( CASE is_working_day WHEN 1 THEN date_d END, 1, NULL )
IGNORE NULLS OVER ( ORDER BY date_d),
CASE is_working_day
WHEN 1
THEN date_d
ELSE LEAD( CASE is_working_day WHEN 1 THEN date_d END, 1, NULL )
IGNORE NULLS OVER ( ORDER BY date_d)
END
) AS last_working_day,
day_of_week_desc_en,
is_working_day
FROM oracle_calendar
DATE_D | LAST_WORKING_DAY | DAY_OF_WEEK_DESC_EN | IS_WORKING_DAY :-------- | :--------------- | :------------------ | -------------: 01-JAN-10 | 04-JAN-10 | Friday | 0 02-JAN-10 | 04-JAN-10 | Saturday | 0 03-JAN-10 | 04-JAN-10 | Sunday | 0 04-JAN-10 | 04-JAN-10 | Monday | 1 05-JAN-10 | 04-JAN-10 | Tuesday | 1 06-JAN-10 | 05-JAN-10 | Wednesday | 1 07-JAN-10 | 06-JAN-10 | Thursday | 1 08-JAN-10 | 07-JAN-10 | Friday | 1 09-JAN-10 | 08-JAN-10 | Saturday | 0 10-JAN-10 | 08-JAN-10 | Sunday | 0
db<>fiddle here
Upvotes: 2