如何在oracle中查找不包括周六、周日和节假日的48个工作时间日期
How to find 48 working hours date excluding saturday,sundays and holidays in oracle
某模块要求求第48、24个工作时间
要求:
假设如果我将 2nd May
作为参数传递给函数,则 48 hours
的输出应为 27th April
,24 hours
的输出应为 28th APRIL
(因为5月1日是假期,4月29日和30日属于周六和周日)
问题出在两个连续的假期上。例如,要创建一个 dummy
数据,我们插入 5 月 2 日作为假期,运行 3rd May
上的代码应该为 48 hours
检索 27th April
和 28th APRIL
对于 24 hours
.
但我的功能似乎不适用于连续的假期。某处计数器增量似乎在错误的位置。
考虑因素:
周末:周六和周日
需要排除的天数:给定节假日日历中的周六、周日和节假日:
假期table创作:
CREATE TABLE HOLIDAY_TAB
(
HOL_DATE DATE,
DESCRIPTION VARCHAR2 (100) DEFAULT NULL
);
insert into HOLIDAY_TAB values (TO_DATE ('26-Jan-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('29-Mar-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('14-Apr-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('01-May-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('02-Jun-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('26-Jun-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('15-Aug-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('25-Aug-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('28-Sep-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('02-Oct-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('19-Oct-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('25-Dec-2017', 'DD-MON-YYYY'),NULL);
commit;
写入捕获假期的函数:
函数:
CREATE OR REPLACE FUNCTION CSE.F_HOL_CHECK_ABC (i_hol_date DATE)
RETURN DATE
AS
valid_working_day DATE := i_hol_date;
day_C holiday_nvs%ROWTYPE;
CURSOR c_hol
IS
SELECT *
FROM HOLIDAY_TAB
WHERE TRUNC (hol_date) = TRUNC (i_hol_date);
CURSOR c_hol_24
IS
SELECT *
FROM HOLIDAY_TAB
WHERE TRUNC (hol_date) = TRUNC (i_hol_date + 3);
CURSOR c_hol_48
IS
SELECT *
FROM HOLIDAY_NVS
WHERE TRUNC (hol_date) = TRUNC (i_hol_date + 2);
BEGIN
-- FOR rec24 IN c_hol_24
-- LOOP
-- IF (rec24.hol_date IS NOT NULL)
-- THEN
-- valid_working_day := i_hol_date - 1;
-- END IF;
-- END LOOP;
OPEN c_hol;
FETCH c_hol INTO day_C;
IF c_hol%FOUND
THEN
SELECT DECODE (TO_CHAR (i_hol_date - 1, 'D'),
1, i_hol_date - 3,
i_hol_date - )
INTO valid_working_day
FROM DUAL;
END IF;
CLOSE c_hol;
RETURN (valid_working_day);
END;
/
不确定功能是否正确。但是有一种奇怪的情况,即与日期文字相比,我的查询在尝试使用 SYSDATE 时没有给出相同的结果。
运行 1 手动设置日期:
SELECT TRUNC (
DECODE (
TO_CHAR (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY'), 'D'),
2, F_HOL_CHECK_ABC (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY') - 4),
DECODE (
TO_CHAR (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY'), 'D'),
3, F_HOL_CHECK_ABC (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY') - 4),
F_HOL_CHECK_ABC (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY') - 2))))
AS "48HOURS",
TRUNC (
DECODE (
TO_CHAR (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY'), 'D'),
2, F_HOL_CHECK_ABC (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY') - 3),
F_HOL_CHECK_ABC (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY') - 1)))
AS "24HOURS"
FROM DUAL;
运行 2 系统日期:
SELECT TRUNC (
DECODE (
TO_CHAR (SYSDATE, 'D'),
2, F_HOL_CHECK_ABC (SYSDATE - 4),
DECODE (TO_CHAR (SYSDATE, 'D'),
3, F_HOL_CHECK_ABC (SYSDATE - 4),
F_HOL_CHECK_ABC (SYSDATE -2)))) as "48 hour" ,
TRUNC (
DECODE (
TO_CHAR (SYSDATE,
'D'),
2, F_HOL_CHECK_ABC (
SYSDATE - 3),
F_HOL_CHECK_ABC (
SYSDATE -1))) as "24 hour" from dual;
非常感谢您提供这方面的帮助。我所需要的只是跳过节假日、星期日和星期六,即;非工作时间,给我每天 48 小时工作时间和 24 小时工作时间
这是使用计数器对代码 2 进行的另一次尝试:
CREATE OR REPLACE FUNCTION CSE.F_HOL_CHECK_S_NS (i_hol_date DATE,
i_S_NS NUMBER)
RETURN DATE
AS
valid_working_day DATE := i_hol_date;
counter NUMBER := 0;
day_number NUMBER := 0;
hol_count NUMBER := 0;
day_C holiday_nvs%ROWTYPE;
CURSOR c_hol (hol_date_c DATE)
IS
SELECT *
FROM HOLIDAY_TAB
WHERE TRUNC (hol_date) = TRUNC (TO_DATE (hol_date_c, 'DD-MON-YYYY'));
BEGIN
IF i_S_NS = 0
THEN
LOOP
IF c_hol%ISOPEN
THEN
CLOSE c_hol;
END IF;
OPEN c_hol (valid_working_day);
IF c_hol%FOUND
THEN
valid_working_day := valid_working_day - 1;
SELECT TO_CHAR (TO_DATE (valid_working_day, 'DD-MON-YYYY'), 'D')
INTO day_number
FROM DUAL;
SELECT COUNT (*)
INTO hol_count
FROM HOLIDAY_TAB
WHERE TRUNC (hol_date) =
TRUNC (TO_DATE (valid_working_day, 'DD-MON-YYYY'));
--valid_working_day:=valid_working_day-1;
-- SELECT DECODE (TO_CHAR (valid_working_day - 1, 'D'),
-- 1, valid_working_day - 3,
-- valid_working_day - 1)
-- INTO valid_working_day
-- FROM DUAL;
IF (hol_count > 0)
THEN
valid_working_day := valid_working_day - 1;
-- counter := counter + 1;
ELSIF (day_number = 1 OR day_number = 7)
THEN
valid_working_day := valid_working_day - 1;
END IF;
ELSIF ( TO_CHAR (TO_DATE (valid_working_day, 'DD-MON-YYYY'), 'D') =
1
OR TO_CHAR (TO_DATE (valid_working_day, 'DD-MON-YYYY'), 'D') =
7)
THEN
valid_working_day := valid_working_day - 1;
ELSE
counter := counter + 1;
valid_working_day := valid_working_day - 1;
END IF;
EXIT WHEN counter >= 3;
END LOOP;
--elsif (i_S_NS <> 0) then
--null;
END IF;
--valid_working_day := valid_working_day - 1;
RETURN (valid_working_day);
END;
这是一个纯粹的 SQL 答案。诀窍是生成一系列涵盖所有可能性的先前日期。在我所知道的国家/地区,连续的假期不超过两个 public(英国的圣诞节和复活节,苏格兰的除夕节)。也考虑到周末,这意味着最多可以排除四天的时间。
正如评论者指出的那样,在其他国家/地区可能会有更长的 public 假期,因此您可能需要相应地调整偏移量。
无论如何,有两天的目标,我们需要一个可以追溯到六天的范围(加上运气)。这将为我们提供目标日期前 7 天的结果集和 :
select (tgt_date - 7) + (level-1)
from dual
connect by level <= 7
现在我们准备好了。我们可以使用带有 'IW'
日期掩码的技巧来将星期几确定为数字,并以文化中立的方式排除星期六和星期日。我们可以左加入 holiday_tab
以排除 public 假期。然后我们对剩下的和 select 最近的两个日期进行排名:
SQL> with hdr as (
2 select dr.dt
3 , case
4 when (1 + dt - trunc(dr.dt, 'IW') in (6,7) then 1
5 when h.hol_date is not null then 1
6 else 0
7 end as hol
8 from ( select trunc(date '2017-05-02' - 7) + (level-1) as dt
9 from dual
10 connect by level <= 7
11 ) dr
12 left join holiday_tab h
13 on h.hol_date = dr.dt
14 )
15 , rhdr as (
16 select hdr.dt
17 , row_number() over (order by hdr.dt desc) rn
18 from hdr
19 where hdr.hol = 0
20 )
21 select rhdr.dt
22 , decode( rhdr.rn, 1, '24hr', '48hr') as cat
23 , to_char(rhdr.dt, 'DY') as dy
24 from rhdr
25 where rn <= 2;
DT CAT DY
--------- ---- ------------
28-APR-17 24hr FRI
27-APR-17 48hr THU
SQL>
鉴于昨天 02-MAY-2017 作为目标日期,这将跳过星期一(五一假期)和周末来识别前两个工作日。
如果你需要一个函数,你可以这样做:
create or replace type dt_nt as table of date;
create or replace function prior_working_days
( p_target_date in date
, p_no_of_days in number := 2)
return dt_nt
is
return_value dt_nt;
offset pls_integer := (p_no_of_days+4+1);
begin
with hdr as (
select dr.dt
, case
when to_char(1 + dt - trunc(dr.dt, 'IW') in (6,7) then 1
when h.hol_date is not null then 1
else 0
end as hol
from ( select (trunc(p_target_date) - offset) + (level-1) as dt
from dual
connect by level <= offset
) dr
left join holiday_tab h
on h.hol_date = dr.dt
)
, rhdr as (
select hdr.dt
, row_number() over (order by hdr.dt desc) rn
from hdr
where hdr.hol = 0
)
select rhdr.dt
bulk collect into return_value
from rhdr
where rn <= p_no_of_days;
return return_value;
end prior_working_days;
/
returns SQL table 个日期:
SQL> select * from table( prior_working_days(sysdate));
COLUMN_VA
---------
02-MAY-17
28-APR-17
SQL>
我的建议是这样的函数:
CREATE OR REPLACE FUNCTION F_HOL_CHECK_S_NS (i_hol_date DATE, i_S_NS NUMBER) RETURN DATE AS
TYPE DATE_TABLE_TYPE is TABLE OF DATE;
Holidays DATE_TABLE_TYPE;
the_date DATE := i_hol_date;
duration INTEGER := 0;
BEGIN
ID i_hol_date IS NULL OR i_S_NS IS NULL THEN
-- Avoid infinite loop
RETURN NULL;
END IF;
-- Just for performance reason
SELECT HOL_DATE
BULK COLLECT INTO Holidays
FROM HOLIDAY_TAB
WHERE HOL_DATE < i_hol_date;
LOOP
the_date := the_date - 1;
IF TO_CHAR(the_date, 'fmDy', 'NLS_DATE_LANGUAGE = american') NOT IN ('Sat', 'Sun') AND TRUNC(the_date) NOT MEMBER OF Holidays THEN
duration := duration + 24;
END IF;
EXIT WHEN duration >= i_S_NS;
END LOOP;
RETURN the_date;
END;
SELECT F_HOL_CHECK_S_NS(DATE '2017-05-02', 24) FROM dual;
SELECT F_HOL_CHECK_S_NS(DATE '2017-05-02', 48) FROM dual;
SELECT F_HOL_CHECK_S_NS(SYSDATE, 24) FROM dual;
SELECT F_HOL_CHECK_S_NS(SYSDATE, 48) FROM dual;
您可以在 SQL 中完成所有操作:
WITH dates ( dt, lvl ) AS (
SELECT CAST( TRUNC( :your_date ) AS DATE ), 0 FROM DUAL
UNION ALL
SELECT CAST( dt - INTERVAL '1' DAY AS DATE ),
CASE
WHEN ( dt - INTERVAL '1' DAY ) - TRUNC( dt - INTERVAL '1' DAY, 'IW' ) >= 5
OR hol_date IS NOT NULL
THEN lvl
ELSE lvl + 1
END
FROM dates d
LEFT OUTER JOIN
holidays h
ON ( d.dt - INTERVAL '1' DAY = h.hol_date )
WHERE lvl < 2
)
SELECT *
FROM dates
PIVOT ( MAX( dt ) FOR lvl IN ( 1 AS DATE24, 2 AS DATE48 ) );
(注意:使用 CAST
不是必需的,但没有我得到 ORA-01790: expression must have same datatype as corresponding expression
)
或者,作为函数:
CREATE OR REPLACE FUNCTION F_HOL_CHECK_S_NS (
i_hol_date DATE,
i_S_NS NUMBER
) RETURN DATE
AS
p_date DATE;
BEGIN
WITH dates ( dt, lvl ) AS (
SELECT CAST( TRUNC( i_hol_date ) AS DATE ), 0 FROM DUAL
UNION ALL
SELECT CAST( dt - INTERVAL '1' DAY AS DATE ),
CASE
WHEN ( dt - INTERVAL '1' DAY ) - TRUNC( dt - INTERVAL '1' DAY, 'IW' ) >= 5
OR hol_date IS NOT NULL
THEN lvl
ELSE lvl + 1
END
FROM dates d
LEFT OUTER JOIN
holidays h
ON ( d.dt - INTERVAL '1' DAY = h.hol_date )
WHERE lvl < i_s_ns
)
SELECT dt
INTO p_date
FROM dates
WHERE lvl = i_s_ns;
RETURN p_date;
END;
/
某模块要求求第48、24个工作时间
要求:
假设如果我将 2nd May
作为参数传递给函数,则 48 hours
的输出应为 27th April
,24 hours
的输出应为 28th APRIL
(因为5月1日是假期,4月29日和30日属于周六和周日)
问题出在两个连续的假期上。例如,要创建一个 dummy
数据,我们插入 5 月 2 日作为假期,运行 3rd May
上的代码应该为 48 hours
检索 27th April
和 28th APRIL
对于 24 hours
.
但我的功能似乎不适用于连续的假期。某处计数器增量似乎在错误的位置。
考虑因素: 周末:周六和周日 需要排除的天数:给定节假日日历中的周六、周日和节假日:
假期table创作:
CREATE TABLE HOLIDAY_TAB
(
HOL_DATE DATE,
DESCRIPTION VARCHAR2 (100) DEFAULT NULL
);
insert into HOLIDAY_TAB values (TO_DATE ('26-Jan-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('29-Mar-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('14-Apr-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('01-May-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('02-Jun-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('26-Jun-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('15-Aug-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('25-Aug-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('28-Sep-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('02-Oct-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('19-Oct-2017', 'DD-MON-YYYY'),NULL);
insert into HOLIDAY_TAB values (TO_DATE ('25-Dec-2017', 'DD-MON-YYYY'),NULL);
commit;
写入捕获假期的函数:
函数:
CREATE OR REPLACE FUNCTION CSE.F_HOL_CHECK_ABC (i_hol_date DATE)
RETURN DATE
AS
valid_working_day DATE := i_hol_date;
day_C holiday_nvs%ROWTYPE;
CURSOR c_hol
IS
SELECT *
FROM HOLIDAY_TAB
WHERE TRUNC (hol_date) = TRUNC (i_hol_date);
CURSOR c_hol_24
IS
SELECT *
FROM HOLIDAY_TAB
WHERE TRUNC (hol_date) = TRUNC (i_hol_date + 3);
CURSOR c_hol_48
IS
SELECT *
FROM HOLIDAY_NVS
WHERE TRUNC (hol_date) = TRUNC (i_hol_date + 2);
BEGIN
-- FOR rec24 IN c_hol_24
-- LOOP
-- IF (rec24.hol_date IS NOT NULL)
-- THEN
-- valid_working_day := i_hol_date - 1;
-- END IF;
-- END LOOP;
OPEN c_hol;
FETCH c_hol INTO day_C;
IF c_hol%FOUND
THEN
SELECT DECODE (TO_CHAR (i_hol_date - 1, 'D'),
1, i_hol_date - 3,
i_hol_date - )
INTO valid_working_day
FROM DUAL;
END IF;
CLOSE c_hol;
RETURN (valid_working_day);
END;
/
不确定功能是否正确。但是有一种奇怪的情况,即与日期文字相比,我的查询在尝试使用 SYSDATE 时没有给出相同的结果。
运行 1 手动设置日期:
SELECT TRUNC (
DECODE (
TO_CHAR (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY'), 'D'),
2, F_HOL_CHECK_ABC (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY') - 4),
DECODE (
TO_CHAR (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY'), 'D'),
3, F_HOL_CHECK_ABC (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY') - 4),
F_HOL_CHECK_ABC (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY') - 2))))
AS "48HOURS",
TRUNC (
DECODE (
TO_CHAR (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY'), 'D'),
2, F_HOL_CHECK_ABC (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY') - 3),
F_HOL_CHECK_ABC (TO_DATE ('03-MAY-2017', 'DD-MON-YYYY') - 1)))
AS "24HOURS"
FROM DUAL;
运行 2 系统日期:
SELECT TRUNC (
DECODE (
TO_CHAR (SYSDATE, 'D'),
2, F_HOL_CHECK_ABC (SYSDATE - 4),
DECODE (TO_CHAR (SYSDATE, 'D'),
3, F_HOL_CHECK_ABC (SYSDATE - 4),
F_HOL_CHECK_ABC (SYSDATE -2)))) as "48 hour" ,
TRUNC (
DECODE (
TO_CHAR (SYSDATE,
'D'),
2, F_HOL_CHECK_ABC (
SYSDATE - 3),
F_HOL_CHECK_ABC (
SYSDATE -1))) as "24 hour" from dual;
非常感谢您提供这方面的帮助。我所需要的只是跳过节假日、星期日和星期六,即;非工作时间,给我每天 48 小时工作时间和 24 小时工作时间
这是使用计数器对代码 2 进行的另一次尝试:
CREATE OR REPLACE FUNCTION CSE.F_HOL_CHECK_S_NS (i_hol_date DATE,
i_S_NS NUMBER)
RETURN DATE
AS
valid_working_day DATE := i_hol_date;
counter NUMBER := 0;
day_number NUMBER := 0;
hol_count NUMBER := 0;
day_C holiday_nvs%ROWTYPE;
CURSOR c_hol (hol_date_c DATE)
IS
SELECT *
FROM HOLIDAY_TAB
WHERE TRUNC (hol_date) = TRUNC (TO_DATE (hol_date_c, 'DD-MON-YYYY'));
BEGIN
IF i_S_NS = 0
THEN
LOOP
IF c_hol%ISOPEN
THEN
CLOSE c_hol;
END IF;
OPEN c_hol (valid_working_day);
IF c_hol%FOUND
THEN
valid_working_day := valid_working_day - 1;
SELECT TO_CHAR (TO_DATE (valid_working_day, 'DD-MON-YYYY'), 'D')
INTO day_number
FROM DUAL;
SELECT COUNT (*)
INTO hol_count
FROM HOLIDAY_TAB
WHERE TRUNC (hol_date) =
TRUNC (TO_DATE (valid_working_day, 'DD-MON-YYYY'));
--valid_working_day:=valid_working_day-1;
-- SELECT DECODE (TO_CHAR (valid_working_day - 1, 'D'),
-- 1, valid_working_day - 3,
-- valid_working_day - 1)
-- INTO valid_working_day
-- FROM DUAL;
IF (hol_count > 0)
THEN
valid_working_day := valid_working_day - 1;
-- counter := counter + 1;
ELSIF (day_number = 1 OR day_number = 7)
THEN
valid_working_day := valid_working_day - 1;
END IF;
ELSIF ( TO_CHAR (TO_DATE (valid_working_day, 'DD-MON-YYYY'), 'D') =
1
OR TO_CHAR (TO_DATE (valid_working_day, 'DD-MON-YYYY'), 'D') =
7)
THEN
valid_working_day := valid_working_day - 1;
ELSE
counter := counter + 1;
valid_working_day := valid_working_day - 1;
END IF;
EXIT WHEN counter >= 3;
END LOOP;
--elsif (i_S_NS <> 0) then
--null;
END IF;
--valid_working_day := valid_working_day - 1;
RETURN (valid_working_day);
END;
这是一个纯粹的 SQL 答案。诀窍是生成一系列涵盖所有可能性的先前日期。在我所知道的国家/地区,连续的假期不超过两个 public(英国的圣诞节和复活节,苏格兰的除夕节)。也考虑到周末,这意味着最多可以排除四天的时间。
正如评论者指出的那样,在其他国家/地区可能会有更长的 public 假期,因此您可能需要相应地调整偏移量。
无论如何,有两天的目标,我们需要一个可以追溯到六天的范围(加上运气)。这将为我们提供目标日期前 7 天的结果集和 :
select (tgt_date - 7) + (level-1)
from dual
connect by level <= 7
现在我们准备好了。我们可以使用带有 'IW'
日期掩码的技巧来将星期几确定为数字,并以文化中立的方式排除星期六和星期日。我们可以左加入 holiday_tab
以排除 public 假期。然后我们对剩下的和 select 最近的两个日期进行排名:
SQL> with hdr as (
2 select dr.dt
3 , case
4 when (1 + dt - trunc(dr.dt, 'IW') in (6,7) then 1
5 when h.hol_date is not null then 1
6 else 0
7 end as hol
8 from ( select trunc(date '2017-05-02' - 7) + (level-1) as dt
9 from dual
10 connect by level <= 7
11 ) dr
12 left join holiday_tab h
13 on h.hol_date = dr.dt
14 )
15 , rhdr as (
16 select hdr.dt
17 , row_number() over (order by hdr.dt desc) rn
18 from hdr
19 where hdr.hol = 0
20 )
21 select rhdr.dt
22 , decode( rhdr.rn, 1, '24hr', '48hr') as cat
23 , to_char(rhdr.dt, 'DY') as dy
24 from rhdr
25 where rn <= 2;
DT CAT DY
--------- ---- ------------
28-APR-17 24hr FRI
27-APR-17 48hr THU
SQL>
鉴于昨天 02-MAY-2017 作为目标日期,这将跳过星期一(五一假期)和周末来识别前两个工作日。
如果你需要一个函数,你可以这样做:
create or replace type dt_nt as table of date;
create or replace function prior_working_days
( p_target_date in date
, p_no_of_days in number := 2)
return dt_nt
is
return_value dt_nt;
offset pls_integer := (p_no_of_days+4+1);
begin
with hdr as (
select dr.dt
, case
when to_char(1 + dt - trunc(dr.dt, 'IW') in (6,7) then 1
when h.hol_date is not null then 1
else 0
end as hol
from ( select (trunc(p_target_date) - offset) + (level-1) as dt
from dual
connect by level <= offset
) dr
left join holiday_tab h
on h.hol_date = dr.dt
)
, rhdr as (
select hdr.dt
, row_number() over (order by hdr.dt desc) rn
from hdr
where hdr.hol = 0
)
select rhdr.dt
bulk collect into return_value
from rhdr
where rn <= p_no_of_days;
return return_value;
end prior_working_days;
/
returns SQL table 个日期:
SQL> select * from table( prior_working_days(sysdate));
COLUMN_VA
---------
02-MAY-17
28-APR-17
SQL>
我的建议是这样的函数:
CREATE OR REPLACE FUNCTION F_HOL_CHECK_S_NS (i_hol_date DATE, i_S_NS NUMBER) RETURN DATE AS
TYPE DATE_TABLE_TYPE is TABLE OF DATE;
Holidays DATE_TABLE_TYPE;
the_date DATE := i_hol_date;
duration INTEGER := 0;
BEGIN
ID i_hol_date IS NULL OR i_S_NS IS NULL THEN
-- Avoid infinite loop
RETURN NULL;
END IF;
-- Just for performance reason
SELECT HOL_DATE
BULK COLLECT INTO Holidays
FROM HOLIDAY_TAB
WHERE HOL_DATE < i_hol_date;
LOOP
the_date := the_date - 1;
IF TO_CHAR(the_date, 'fmDy', 'NLS_DATE_LANGUAGE = american') NOT IN ('Sat', 'Sun') AND TRUNC(the_date) NOT MEMBER OF Holidays THEN
duration := duration + 24;
END IF;
EXIT WHEN duration >= i_S_NS;
END LOOP;
RETURN the_date;
END;
SELECT F_HOL_CHECK_S_NS(DATE '2017-05-02', 24) FROM dual;
SELECT F_HOL_CHECK_S_NS(DATE '2017-05-02', 48) FROM dual;
SELECT F_HOL_CHECK_S_NS(SYSDATE, 24) FROM dual;
SELECT F_HOL_CHECK_S_NS(SYSDATE, 48) FROM dual;
您可以在 SQL 中完成所有操作:
WITH dates ( dt, lvl ) AS (
SELECT CAST( TRUNC( :your_date ) AS DATE ), 0 FROM DUAL
UNION ALL
SELECT CAST( dt - INTERVAL '1' DAY AS DATE ),
CASE
WHEN ( dt - INTERVAL '1' DAY ) - TRUNC( dt - INTERVAL '1' DAY, 'IW' ) >= 5
OR hol_date IS NOT NULL
THEN lvl
ELSE lvl + 1
END
FROM dates d
LEFT OUTER JOIN
holidays h
ON ( d.dt - INTERVAL '1' DAY = h.hol_date )
WHERE lvl < 2
)
SELECT *
FROM dates
PIVOT ( MAX( dt ) FOR lvl IN ( 1 AS DATE24, 2 AS DATE48 ) );
(注意:使用 CAST
不是必需的,但没有我得到 ORA-01790: expression must have same datatype as corresponding expression
)
或者,作为函数:
CREATE OR REPLACE FUNCTION F_HOL_CHECK_S_NS (
i_hol_date DATE,
i_S_NS NUMBER
) RETURN DATE
AS
p_date DATE;
BEGIN
WITH dates ( dt, lvl ) AS (
SELECT CAST( TRUNC( i_hol_date ) AS DATE ), 0 FROM DUAL
UNION ALL
SELECT CAST( dt - INTERVAL '1' DAY AS DATE ),
CASE
WHEN ( dt - INTERVAL '1' DAY ) - TRUNC( dt - INTERVAL '1' DAY, 'IW' ) >= 5
OR hol_date IS NOT NULL
THEN lvl
ELSE lvl + 1
END
FROM dates d
LEFT OUTER JOIN
holidays h
ON ( d.dt - INTERVAL '1' DAY = h.hol_date )
WHERE lvl < i_s_ns
)
SELECT dt
INTO p_date
FROM dates
WHERE lvl = i_s_ns;
RETURN p_date;
END;
/