在oracle plsql中添加n个工作日,包括自定义假日列表
我需要在oracle plsql函数中查找第n个工作日,该函数应排除周末和自定义假日列表。我得到了这样的东西,但无法适应这里的定制假日逻辑在oracle plsql中添加n个工作日,包括自定义假日列表,sql,oracle,plsql,Sql,Oracle,Plsql,我需要在oracle plsql函数中查找第n个工作日,该函数应排除周末和自定义假日列表。我得到了这样的东西,但无法适应这里的定制假日逻辑 create or replace function add_n_working_days ( start_date date, working_days pls_integer ) return date as end_date date := start_date; counter pls_integer := 0; begin i
create or replace function add_n_working_days (
start_date date, working_days pls_integer
) return date as
end_date date := start_date;
counter pls_integer := 0;
begin
if working_days = 0 then
end_date := start_date;
elsif to_char(start_date, 'fmdy') in ('sat', 'sun') then
end_date := next_day(start_date, 'monday');
end if;
while (counter < working_days) loop
end_date := end_date + 1;
if to_char(end_date, 'fmdy') not in ('sat', 'sun') then
counter := counter + 1;
end if;
end loop;
return end_date;
end add_n_working_days;
/
我尝试使用子查询添加elsif条件,但不支持
if to_char(end_date, 'fmdy') not in ('sat', 'sun') then
counter := counter + 1;
elsif to_char(end_date, 'YYYYMMDD') in (select holidays from holiday_table) then
counter := counter + 1;
end if;
我将尝试使用类似的循环计算周末天数:
WHILE (v_copy > 0) LOOP
end_date := end_date + 1;
IF to_char(end_date, 'fmdy') IN ('sat', 'sun') THEN
end_date := end_date + 1;
ELSE
v_copy := v_copy - 1;
END IF;
END LOOP;
然后在循环过程之后,您可以计算表中在开始日期和结束日期之后,而不是在“sat”、“sun”中的假期数
现在将此数字添加到您的结束日期,您将获得第n个工作日的日期。当然,在返回之前,请检查新的结束日期是否在“sat”、“sun”中
最后,结束日期是第n个工作日的日期
CREATE OR REPLACE FUNCTION add_n_working_days (
start_date DATE,
working_days PLS_INTEGER
) RETURN DATE AS
end_date DATE := start_date;
counter PLS_INTEGER := 0;
v_copy PLS_INTEGER := working_days;
v_custom_cnt INTEGER := 0;
BEGIN
IF working_days = 0 THEN
end_date := start_date;
ELSIF to_char(start_date, 'fmdy') IN ('sat', 'sun') THEN
end_date := next_day(start_date, 'monday');
END IF;
WHILE (v_copy > 0) LOOP
end_date := end_date + 1;
IF to_char(end_date, 'fmdy') IN ('sat', 'sun') THEN
end_date := end_date + 1;
ELSE
v_copy := v_copy - 1;
END IF;
END LOOP;
SELECT COUNT(*) INTO v_custom_cnt
FROM holiday_table
WHERE to_char(end_date, 'fmdy') NOT IN ('sat', 'sun')
AND holidays BETWEEN start_date AND end_date;
end_date := end_date + v_custom_cnt;
IF to_char(end_date, 'fmdy') IN ('sat', 'sun') THEN
end_date := next_day(start_date, 'monday');
END IF;
RETURN end_date;
END add_n_working_days;
它不是100%测试的不同的方法:创建一个只列出工作日并取第n个值的表:
CREATE OR REPLACE FUNCTION add_n_working_days (
start_date DATE, working_days PLS_INTEGER
) RETURN DATE AS
l_end_date DATE := start_date;
l_counter pls_integer := 0;
BEGIN
SELECT
business_day
INTO l_end_date
FROM
(
WITH
dates AS
(SELECT start_date + level - 1 as dt FROM dual CONNECT BY level < 100)
,weekdates AS
(SELECT dt as weekday FROM dates WHERE TO_CHAR(dt,'fmdy') NOT IN ('sat','sun'))
,business_days AS
(
SELECT weekday as business_day FROM weekdates
MINUS
SELECT holiday FROM so_holidays
)
SELECT business_day, ROW_NUMBER() OVER (ORDER BY 1) as rn from business_days
)
WHERE rn = working_days + 1;
RETURN l_end_date;
END add_n_working_days;
SQL是面向集合的,所以开始考虑集合,而不是迭代和循环。假设您可以指定要测试的最大天数,那么只需一个简单的查询就可以得到您想要的结果
create or replace function add_n_working_days (
start_date date, working_days pls_integer
) return date as
working_date_ot date;
begin
with date_list as (select trunc(start_date) + level - 1 tdate
from dual
connect by level <= 5*working_days
)
select tdate
into working_date_ot
from ( select tdate, row_number() over(order by tdate) date_num
from date_list
where to_char(tdate,'fmdy') not in ('sat','sun')
and not exists
(select null
from holiday_dates
where trunc(holiday_date) = tdate
)
)
where date_num = working_days;
return working_date_ot;
end add_n_working_days;
工作原理:
该函数假定所需日期在请求天数的5倍内。因此,如果要求10项业务,那么最终的日期将在50天内,如果不这样做,那将是一大堆假期
日期列表CTE建立了一个潜在日期列表,达到确定的限制。
主菜单中的子选项过滤生成的列表,消除Sat和Sun。然后,它在假日表中查找剩余的日期,并删除表中的任何日期。
剩余日期根据升序值进行编号。
然后,主外部选择选择与指定工作日匹配的日期编号
不需要循环。只值我的0.02美分 我需要找到“第n个工作日”是否存在永远不会超过的n值?如果您的假日列表,即假日表中的行数不是很长,请将其批量收集到pl/sql表中,然后您可以使用look-at[此帖子]的成员检查结束日期是否存在
CREATE OR REPLACE FUNCTION add_n_working_days (
start_date DATE,
working_days PLS_INTEGER
) RETURN DATE AS
end_date DATE := start_date;
counter PLS_INTEGER := 0;
v_copy PLS_INTEGER := working_days;
v_custom_cnt INTEGER := 0;
BEGIN
IF working_days = 0 THEN
end_date := start_date;
ELSIF to_char(start_date, 'fmdy') IN ('sat', 'sun') THEN
end_date := next_day(start_date, 'monday');
END IF;
WHILE (v_copy > 0) LOOP
end_date := end_date + 1;
IF to_char(end_date, 'fmdy') IN ('sat', 'sun') THEN
end_date := end_date + 1;
ELSE
v_copy := v_copy - 1;
END IF;
END LOOP;
SELECT COUNT(*) INTO v_custom_cnt
FROM holiday_table
WHERE to_char(end_date, 'fmdy') NOT IN ('sat', 'sun')
AND holidays BETWEEN start_date AND end_date;
end_date := end_date + v_custom_cnt;
IF to_char(end_date, 'fmdy') IN ('sat', 'sun') THEN
end_date := next_day(start_date, 'monday');
END IF;
RETURN end_date;
END add_n_working_days;
CREATE OR REPLACE FUNCTION add_n_working_days (
start_date DATE, working_days PLS_INTEGER
) RETURN DATE AS
l_end_date DATE := start_date;
l_counter pls_integer := 0;
BEGIN
SELECT
business_day
INTO l_end_date
FROM
(
WITH
dates AS
(SELECT start_date + level - 1 as dt FROM dual CONNECT BY level < 100)
,weekdates AS
(SELECT dt as weekday FROM dates WHERE TO_CHAR(dt,'fmdy') NOT IN ('sat','sun'))
,business_days AS
(
SELECT weekday as business_day FROM weekdates
MINUS
SELECT holiday FROM so_holidays
)
SELECT business_day, ROW_NUMBER() OVER (ORDER BY 1) as rn from business_days
)
WHERE rn = working_days + 1;
RETURN l_end_date;
END add_n_working_days;
create or replace function add_n_working_days (
start_date date, working_days pls_integer
) return date as
working_date_ot date;
begin
with date_list as (select trunc(start_date) + level - 1 tdate
from dual
connect by level <= 5*working_days
)
select tdate
into working_date_ot
from ( select tdate, row_number() over(order by tdate) date_num
from date_list
where to_char(tdate,'fmdy') not in ('sat','sun')
and not exists
(select null
from holiday_dates
where trunc(holiday_date) = tdate
)
)
where date_num = working_days;
return working_date_ot;
end add_n_working_days;