在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

我需要在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

  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;