pl/sql 函数的递归调用(时间操作)

工具 : PLSQL Developer

要求:

      给定一个时间(date 类型, yyyy-MM-dd HH:mm:ss),求过了n个星期后,此时间的下一个月的时间.

CREATE OR REPLACE FUNCTION FN_RECURSIVE_DATE(EXEC_TIME IN DATE,   -- 执行时间
                                             V_CYCLE   IN NUMBER, -- n周
                                             OUT_DATE  OUT DATE)  -- 执行时间执行n周后的下一个月时间
  RETURN DATE AS

  /*按周期赠送,按周的递归函数,找到配置的执行时间的下一个月*/

  V_EXEC_DAY      NUMBER(2); -- 执行时间EXEC_TIME中的天
  V_EXEC_MONTH    NUMBER(2); -- 执行时间EXEC_TIME中的月份
  V_EXEC_YEAR     VARCHAR2(4); -- 执行时间EXEC_TIME中的年份
  V_DAYS          NUMBER(4); -- 总天数
  V_STR_EXEC_TIME VARCHAR2(20); -- 执行时间EXEC_TIME的字符串表示方式
  V_SPACE_INDEX   NUMBER(2); -- 执行时间EXEC_TIME中空格所在索引
  V_OTHERS_TIME   VARCHAR2(20); -- EXEC_TIME的格式' HH:mm:ss'
  V_MAX_DAY       NUMBER(2); -- 某个月的最大天数
  V_CUR_YEAR      VARCHAR2(4); -- 当前年份
  V_CUR_MONTH     NUMBER(2); -- 当前月份
  V_N_EXEC_YEAR   NUMBER(4); -- V_EXEC_YEAR 的number格式
  V_NEXT_MONTH    NUMBER(2); -- 下一月
  V_N_CUR_YEAR    NUMBER(4); -- v_cur_year 的number格式
BEGIN
  -- 取出execTime的day
  V_EXEC_DAY := FN_GET_DAY_OF_DATE(EXEC_TIME, V_EXEC_DAY);
  -- 取出执行时间execTime中的月份
  V_EXEC_MONTH := F_GET_MONTH(EXEC_TIME, V_EXEC_MONTH);
  -- execTime 的年份
  V_EXEC_YEAR := FN_GET_YEAR_OF_DATE(EXEC_TIME, V_EXEC_YEAR);
  -- n周的天数+执行时间中的day
  V_DAYS := V_EXEC_DAY + V_CYCLE * 7;
  -- 将exec_time转成string格式
  V_STR_EXEC_TIME := TO_CHAR(EXEC_TIME, 'yyyy-mm-dd hh24:mi:ss');
  -- 找出exec_time中空格的位置
  V_SPACE_INDEX := INSTR(V_STR_EXEC_TIME, ' ', 1, 1);
  -- ' HH:mm:ss'
  V_OTHERS_TIME := SUBSTR(V_STR_EXEC_TIME, V_SPACE_INDEX);
  -- 某个月的最大天数
  V_MAX_DAY := FN_MAXDAYOFMONTH(V_EXEC_MONTH, V_EXEC_YEAR, V_MAX_DAY);

  IF V_DAYS > V_MAX_DAY THEN
    /* 如果总天数大于该月的最大天数 */

    V_EXEC_MONTH := V_EXEC_MONTH + 1;

    IF V_EXEC_MONTH > 12 THEN
      V_EXEC_MONTH := 1;
      V_EXEC_YEAR  := TO_CHAR(TO_NUMBER(V_EXEC_YEAR) + 1);
    END IF;
    -- end of IF V_EXEC_MONTH > 12

    V_EXEC_DAY := V_DAYS - V_MAX_DAY;

    -- 获取当前月份
    V_CUR_MONTH := F_GET_MONTH(SYSDATE, V_CUR_MONTH);

    -- 执行年份的number格式
    V_N_EXEC_YEAR := TO_NUMBER(V_EXEC_YEAR);

    V_NEXT_MONTH := V_CUR_MONTH + 1;

    IF V_NEXT_MONTH > 12 THEN
      V_NEXT_MONTH  := 1;
      V_N_EXEC_YEAR := V_N_EXEC_YEAR + 1;
    END IF;
    -- end of IF v_next_month > 12

    -- 获取当前年份
    V_CUR_YEAR := FN_GET_YEAR_OF_DATE(SYSDATE, V_CUR_YEAR);

    V_N_CUR_YEAR := TO_NUMBER(V_CUR_YEAR);

    IF (V_N_CUR_YEAR = V_N_EXEC_YEAR OR V_N_CUR_YEAR + 1 = V_N_EXEC_YEAR) AND
       V_NEXT_MONTH = V_EXEC_MONTH THEN
      /* 是下一个月 */
      V_STR_EXEC_TIME := V_EXEC_YEAR || '-' || V_EXEC_MONTH || '-' ||
                         V_EXEC_DAY || V_OTHERS_TIME;

      OUT_DATE := TO_DATE(V_STR_EXEC_TIME, 'yyyy-mm-dd hh24:mi:ss');

      RETURN OUT_DATE;

    END IF;

  ELSE
    /* 没到下一个月 */ 
    -- 这里的day写的是 V_DAYS (相加后的总天数) , 不然会出现死循环
    V_STR_EXEC_TIME := V_EXEC_YEAR || '-' || V_EXEC_MONTH || '-' ||
                       V_DAYS || V_OTHERS_TIME;

    OUT_DATE := TO_DATE(V_STR_EXEC_TIME, 'yyyy-mm-dd hh24:mi:ss');
    
    -- 递归调用
    RETURN FN_RECURSIVE_DATE(OUT_DATE, V_CYCLE, OUT_DATE);

  END IF;
  -- end of if V_DAYS > V_MAX_DAY

EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('execute FN_RECURSIVE_DATE fail!' || SQLERRM);
END;
/
 

执行:

  在工具 PLSQL Developer 的Command Window 中输入以下的代码:

VAR v_date DATE;
EXEC :v_date := FN_RECURSIVE_DATE(SYSDATE,2,:v_date);
 

执行结果如下所示:


猜你喜欢

转载自xurichusheng.iteye.com/blog/1672993