关于plsql:在Oracle中获取第n个工作日期

 2021-04-26 

Get nth working date in Oracle

我们已经在SQL中提供了一个函数,当您传递Startdate,Enddate和Workday(nth)时,它会计算第n个工作日(不包括周末和节假日)。它为您提供第n个工作日...

正在运行的SQL函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
FUNCTION [dbo].[getNthWorkingDate]
   (
@StartDate AS datetime,
@EndDate AS datetime,
@WorkDay AS int
  )
    RETURNS datetime
    AS
    BEGIN
-- Declare the return variable here
DECLARE @WorkDate AS datetime, @LeaveYear AS SMALLINT,@iCount AS int
SET @LeaveYear = datepart(YEAR, @StartDate)
SET @iCount = 1
WHILE (@StartDate < @EndDate)
    BEGIN  

        IF (DATENAME(WEEKDAY,@StartDate )  = 'SUNDAY') OR (DATENAME(WEEKDAY,@StartDate )  = 'SATURDAY')
            -- Just to keep the if statement with out code
            SET @iCount = @iCount;
        ELSE IF EXISTS (SELECT * FROM HOLIDAYS WHERE CAST (HOLIDAY + ' ' + CAST(@LeaveYear AS VARCHAR) AS DATETIME) = @StartDate)
            -- Just to keep the if statement with out code
            SET @iCount = @iCount;
        ELSE
            BEGIN
                SET @WorkDate = @StartDate
                IF @iCount = @WorkDay
                    BREAK;
                ELSE
                    SET @iCount = @iCount + 1;
            END

        SET @StartDate = dateadd(DAY, 1, @StartDate );
    END
-- Return the result of the function
RETURN @WorkDate
END

我正在尝试在ORACLE(oracle的新手)中重新创建此函数,我进行了一些更改,但无法正常工作,我认为我在循环中缺少某些内容,将不胜感激...在此先感谢..

ORACLE功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
CREATE OR REPLACE FUNCTION GETNTHWORKINGDATE (pStartDate DATE,
                                          pEndDate DATE,
                                          pWorkDay NUMBER)
  RETURN DATE
  AS
  vStartDate DATE;
  vWorkDate DATE ;
  vCount NUMBER;
  vHoliday DATE;
  BEGIN
   vCount := 1;
  BEGIN
  SELECT HOLIDAY_DATE INTO vHoliday FROM HOLIDAY WHERE (TO_CHAR(HOLIDAY_DATE, 'MM DD') = TO_CHAR(pStartDate, 'MM DD'));
  EXCEPTION
   WHEN NO_DATA_FOUND THEN      
    vHoliday := NULL;  
 END;    
 vStartDate := pStartDate;

BEGIN
WHILE (vStartDate < pEndDate)
LOOP
   IF (TO_CHAR(vStartDate, 'DAY') = 'SUNDAY' ) OR (TO_CHAR(vStartDate, 'DAY') = 'SATURDAY') THEN
      vCount := vCount;
   ELSIF (TO_CHAR(vHoliday, 'MM DD') = TO_CHAR(vStartDate, 'MM DD')) THEN
      vCount := vCount;
   ELSE      
      vWorkDate := vStartDate;
      IF vCount = pWorkDay THEN
        EXIT;
      ELSE  
      vCount := vCount + 1;            
    END IF;
  END IF;
      vStartDate := vStartDate + 1;
END LOOP;

END;

RETURN vWorkDate;

END GETNTHWORKINGDATE;


类似的事情适用-在普通SQL中。如果您真的不需要使用PL / SQL,最好不要使用它。如果确实需要使用它,请根据需要进行调整。我在评论中看到您已经更改了需求;根据需要进行调整。

"魔术数字" 2 * :wd_number + 5用于确保我们添加足够的日历日期,以包括至少:wd_number个工作日; +5用于:wd_number的低值。这不是最有效的解决方案,但是它浪费的时间不超过几毫秒,因此我没有费心去提高它的效率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
WITH holidays( holiday_date, holiday_name ) AS (
       SELECT DATE '2016-01-01', 'New Year''s Day'   FROM dual UNION ALL
       SELECT DATE '2016-04-01', 'April Fools'' Day' FROM dual UNION ALL
       SELECT DATE '2016-05-01', 'May First'         FROM dual
     ),
     work_days ( dt ) AS (
       SELECT  TO_DATE(:start_date, 'yyyy-mm-dd') + LEVEL - 1
         FROM  dual
         WHERE TO_CHAR(TO_DATE(:start_date, 'yyyy-mm-dd') + LEVEL - 1, 'Dy')
                                                                    NOT IN ('Sat', 'Sun')
         CONNECT BY LEVEL < 2 * TO_NUMBER(:wd_number) + 5
       MINUS
       SELECT  holiday_date
         FROM  holidays
     ),
     ordered_work_days ( dt, rn ) AS (
       SELECT dt, ROW_NUMBER() over (ORDER BY dt)
       FROM   work_days
     )
SELECT dt
FROM   ordered_work_days
WHERE  rn = TO_NUMBER(:wd_number)
;


------排除周末并计算第N个工作日
选择--CURRENTDATE
WORK_DATE,
WORK_DAY,
ROW_NUMBER()已于NTH_WORKING_DAY(在WORK_DATE之前订购)

(
-按日期获取所有日期
选择货币,
FIRST_DATE(级别-1),截至WORK_DATE,
TO_CHAR(FIRST_DATE(LEVEL-1),'DAY')AS WORK_DAY

(
-获取每月的当前日期,第一个日期和最后一个日期
SELECT sysdate CURRENTDATE,
TRUNC(sysdate,'MM')AS FIRST_DATE,
LAST_DAY(sysdate)LAST_DATE
从双重
)
CONNECT FIRST_DATE(级别-1)<= LAST_DATE ) TRIM(WORK_DAY)不在('SATURDAY','SUNDAY');


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
      CREATE OR REPLACE FUNCTION GETNTHWORKINGDATE (pStartDate DATE,
                                          pEndDate DATE,
                                          pWorkDay NUMBER)
 RETURN DATE AS
 vStartDate DATE;
 vWorkDate DATE ;
 vCount NUMBER;
 vHoliday DATE;

BEGIN
vCount := 1;
vStartDate := pStartDate;  
BEGIN
WHILE (vStartDate < pEndDate)
  LOOP
      BEGIN
          --Select Holiday Month and Date into vHoliday Variable when Start Date is a holiday.
           SELECT HOLIDAY_DATE INTO vHoliday FROM HOLIDAY WHERE (TO_CHAR(HOLIDAY_DATE, 'MM DD') = TO_CHAR(vStartDate, 'MM DD'));
           EXCEPTION
           WHEN NO_DATA_FOUND THEN vHoliday := NULL;  
       END;  

    -- Code to eliminate Weekends
    IF (TO_CHAR(vStartDate, 'D') = 1 ) OR (TO_CHAR(vStartDate, 'D') = 7) THEN
        vCount := vCount;

    --Code to eliminate Holiday's from holiday table.
    ELSIF (TO_CHAR(vHoliday, 'MM DD') = TO_CHAR(vStartDate, 'MM DD')) THEN
       vCount := vCount;
    ELSE      
       vWorkDate := vStartDate;
      IF vCount = pWorkDay THEN
        EXIT;
      ELSE  
        vCount := vCount + 1;            
      END IF;
   END IF;
   vStartDate := vStartDate + 1;
   END LOOP;      
  END;

 RETURN vWorkDate;

END GETNTHWORKINGDATE;