【问题标题】:get work days function to return whole number on sunday获取工作日函数以在星期日返回整数
【发布时间】:2021-12-31 14:58:37
【问题描述】:

函数 number_of_days(start_date, end_date) 应该计算 start_date 和 end_date 之间的工作日数。只要不是星期日,它适用于任何 start_date。

计算有3个步骤:

  1. 取整周前的天数(对于 21 日至 24 日等小范围日期,整周可以为 0)

  2. 然后它会增加整周

  3. 将整周后剩余的所有天数加起来。

我需要第二步来生成像 0、5、10 这样的整数,而不是在这个例子中我得到 0,7142857142857142857142857142857142857,因为 start_date 参数是星期天。

我可以使用 ROUND() 来解决它,但也许有更好的方法?

    CREATE OR REPLACE FUNCTION number_of_days(start_date IN DATE, end_date IN DATE)
    RETURN  NUMBER
    
    IS  v_number_of_days NUMBER;
        first_week_day DATE := TO_DATE('31-12-2017', 'DD-MM-YYYY');
    
    BEGIN
--step 1
    SELECT  ( CASE  WHEN MOD(start_date - first_week_day, 7) BETWEEN 2 AND 5
                    THEN 6 - MOD(start_date - first_week_day, 7)
                    ELSE 0  END )
            +
--step 2
            ((  CASE  WHEN MOD(end_date - first_week_day, 7) < 7
                      THEN end_date - MOD(end_date - first_week_day, 7)
                      ELSE end_date  END )
              -
              ( CASE  WHEN MOD(start_date - first_week_day, 7) > 1
                      THEN start_date + 8 - MOD(start_date - first_week_day, 7)
                      ELSE start_date  END ) + 1
            ) / 7 * 5
            +
--step 3
            ( CASE  WHEN MOD(end_date - first_week_day,7) BETWEEN 1 AND 6
                    THEN CASE   WHEN MOD(end_date - first_week_day, 7) = 6
                                THEN MOD(end_date - first_week_day, 7) - 1
                                ELSE MOD(end_date - first_week_day, 7) END
                    ELSE 0 END  )
    INTO    v_number_of_days
    FROM    DUAL;
    
    RETURN  v_number_of_days;
    
    END;
    

    --test
    SELECT  number_of_days(TO_DATE('21-11-2021', 'DD-MM-YYYY'), TO_DATE('24-11-2021', 'DD-MM-YYYY'))
    FROM    DUAL;

【问题讨论】:

  • javascript 在所有这些中起到什么作用?

标签: sql oracle plsql oracle11g


【解决方案1】:

你只是把事情弄得太复杂了...... 首先,最好在 ISO 周内工作:从星期一开始。

因此,您可以使用trunc(dt, 'IW') 轻松获得 ISO 周的第一天。例如,11 月 19 日是星期五,我们可以使用 trunc(date'2021-11-19','IW') 轻松获取本周的第一天:

SQL> select trunc(date'2021-11-19','IW') xx from dual;

XX
-------------------
2021-11-15 00:00:00

所以 2021-11-15 是星期一。

现在让我们假设我们有一些日期:

 Mo Tu We Th Fr Sa Su
 -- -- -- -- -- -- --
           X  X  X  X
  X  X  X  X  X  X  X
  X  X  X  X  X  X  X
  X  X  X  X  X  X  X
  X  X  X  X  X  X  X
  X  X  X  X  X  X  X
  X  X  X  X  X  X  X
  X  X

我们可以很容易地得到整周的工作日数(count_full_weeks * 5/7),所以让我们排除它们:

 Mo Tu We Th Fr Sa Su
 -- -- -- -- -- -- --
           X  X  X  X
  X  X

然后,让我们以图形方式对其进行转换,因为我们知道剩余天数将始终少于 7 天(少于一周),并且第一天将是前 7 天之一:

 Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su
 -- -- -- -- -- -- -- -- -- -- -- -- -- --
           X  X  X  X  X  X

让我们用他们的数字替换 Mo,Tu,We...:

  0  1  2  3  4  5  6  0  1  2  3  4  5  6
 -- -- -- -- -- -- -- -- -- -- -- -- -- --
           X  X  X  X  X  X

由于我们知道这部分周的天数不可能是 7,因此我们知道最晚的结束时间是:

  0  1  2  3  4  5  6  0  1  2  3  4  5  6
 -- -- -- -- -- -- -- -- -- -- -- -- -- --
                    X  X  X  X  X  X

现在让我们将第二周的数字替换为 7-13:

  0  1  2  3  4  5  6  7  8  9 10 11 12 13
 -- -- -- -- -- -- -- -- -- -- -- -- -- --
                    X  X  X  X  X  X

现在我们可以理解,我们只需要检查第 5 天和第 6 天,即检查我们的周期是否包含 5 和 6。

所以会是这样(我已经尽可能详细了):

CREATE OR REPLACE FUNCTION number_of_days(start_date IN DATE, end_date IN DATE)
    RETURN  NUMBER
as
    days_between        int;
    full_weeks          int;
    remaining_days      int;
    remaining_workdays  int;
    
    v_first_week_day    date;
    v_start             int;
    v_end               int;
    
    result              int;
begin
    days_between := end_date - start_date;
    full_weeks := trunc(days_between/7);
    remaining_days:= mod(days_between,7); -- or days_between-full_weeks*7
    
    v_first_week_day := trunc(start_date,'IW');
    v_start := start_date - v_first_week_day;
    v_end := v_start + remaining_days - 1;
    
    remaining_workdays := remaining_days
                        - case when 5 between v_start and v_end then 1 else 0 end
                        - case when 6 between v_start and v_end then 1 else 0 end
                        ;
    result := full_weeks * 5 + remaining_workdays;
    RETURN result;
end;
/

我做了很多子步骤和中间计算和变量,只是为了让它更清楚。显然,你可以让它更短,例如:

CREATE OR REPLACE FUNCTION number_of_days(start_date IN DATE, end_date IN DATE)
    RETURN  NUMBER
as
begin
    return 5*trunc((end_date-start_date)/7) + mod((end_date-start_date),7) 
     - case when 5-(start_date - trunc(start_date,'IW')) between 0 and mod((end_date-start_date),7)-1 then 1 else 0 end
     - case when 6-(start_date - trunc(start_date,'IW')) between 0 and mod((end_date-start_date),7)-1 then 1 else 0 end
    ;
end;
/

【讨论】:

    【解决方案2】:

    这应该可以帮助您入门,因为我不了解您的要求。

    with time_between as (
      select trunc ( timestamp'2021-06-14 06:00:00' ) - 
               trunc ( timestamp'2021-06-08 14:00:00' ) t
      from   dual
    )
      select *
      from   time_between;
    
    T   
       6
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多