【问题标题】:Find the fifth working day between 2 dates sql查找两个日期之间的第五个工作日sql
【发布时间】:2020-04-03 10:58:11
【问题描述】:

我正在尝试查找两个日期之间的第五个工作日,不包括节假日。

这是我写的查询:

with s_date as (select TO_DATE('21-04-2020','DD-MM-YYYY') d from dual),
     e_date as (select TO_DATE('01-05-2020','DD-MM-YYYY') d from dual),
     no_of_days as (select abs(trunc(s_date.d -  e_date.d))+1 no from s_date,e_date),
    cal as (select d+rownum-1 dt
            from s_date
            connect by level <= (select no from no_of_days) )
select cal.dt  from cal where to_char(cal.dt, 'DY', 'NLS_DATE_LANGUAGE=ENGLISH') NOT IN ('SAT', 'SUN')
and cal.dt not in (select day from T_PUBLIC_HOLIDAYS) order by cal.dt desc;

输出:

DT
1:30-04-2020
2:29-04-2020
3:28-04-2020
4:27-04-2020
5:24-04-2020
6:23-04-2020
7:22-04-2020
8:21-04-2020

如何将输出限制为: DT 1: 24-04-2020

【问题讨论】:

    标签: sql oracle date


    【解决方案1】:

    对于第五个,您只需在请求的末尾添加:

    fetch first 5 rows only
    

    您可以只使用一行(然后结束...):

    fetch first row only
    

    您首先选择第 5 行(按日期降序排列),然后选择唯一的第一行,按日期升序排列前一个结果。

    你的 SQL 变成了:

    select * from (
    with s_date as (select TO_DATE('21-04-2020','DD-MM-YYYY') d from dual),
         e_date as (select TO_DATE('01-05-2020','DD-MM-YYYY') d from dual),
         no_of_days as (select abs(trunc(s_date.d -  e_date.d))+1 no from s_date,e_date),
        cal as (select d+rownum-1 dt
                from s_date
                connect by level <= (select no from no_of_days) )
    select cal.dt  from cal where to_char(cal.dt, 'DY', 'NLS_DATE_LANGUAGE=ENGLISH') NOT IN ('SAT', 'SUN')
    and cal.dt not in (select day from T_PUBLIC_HOLIDAYS)
    order by cal.dt desc
    fetch first 5 rows only
    ) x
    order by x.dt
    fetch first row only
    ;
    

    【讨论】:

      【解决方案2】:

      请检查以下查询,让我知道您的问题是否已解决。

      with s_date as (select TO_DATE('21-04-2020','DD-MM-YYYY') d from dual),
           e_date as (select TO_DATE(DT_EXPR_PLDG,'DD-MM-YYYY') d from T_ANCRDT_FNNCL_C_FR where DT_EXPR_PLDG is not null ),
           no_of_days as (select abs(trunc(s_date.d -  e_date.d))+1 no from s_date,e_date),
          cal as (select d+rownum-1 dt
                  from s_date
                  connect by level <= (select no from no_of_days) )
      select cal.dt  from cal where to_char(cal.dt, 'DY', 'NLS_DATE_LANGUAGE=ENGLISH') NOT IN ('SAT', 'SUN')
      AND to_char (date '21-04-2020', 'D')=5 -- you can add weekday() function as well 
      and cal.dt not in (select day from T_PUBLIC_HOLIDAYS) 
      order by cal.dt desc;
      

      【讨论】:

      • SQL 不起作用。 TO_CHAR('21-04-2020'D)=4 给出 ORA-00907 错误...
      • 您正在使用 SQL 数据库或 oracle,如果是 SQL 数据库,那么您可以使用 DATEPART(WEEKDAY, '21-04-2020')
      • 标签说“oracle”。认为这是一个 Oracle SQL 请求。
      • 你可以用Weekday()代替这个表达式TO_CHAR('21-04-2020'D)=4
      • 我只是不明白为什么你的解决方案解决了这个问题。这个“工作日”功能是什么?据我所知,不是 Oracle 函数。
      【解决方案3】:

      将它放到另一个 SELECT 语句中并使用 ROWNUM。

      select date_value FROM (
      select cal.dt as date_value, ROWNUM as row_num from cal where to_char(cal.dt, 'DY', 'NLS_DATE_LANGUAGE=ENGLISH') NOT IN ('SAT', 'SUN')
      and cal.dt not in (select day from T_PUBLIC_HOLIDAYS) order by cal.dt desc
      )) WHERE row_num = 5
      

      顺便说一句,你的整个 SQL 语句很丑,你不应该使用它:)

      【讨论】:

      • 丑陋但在开发这么多的 Oracle 版本。我的适用于最近的。
      【解决方案4】:

      首先创建附加列以对结果中的日期进行排序,然后选择所需的任何日期,在您的情况下为 day_order = 6

      select 
          cal.dt 
          , rank() over (order by cal.dt) as day_order    
      from cal 
      where to_char(cal.dt, 'dy', 'nls_date_language=english') not in ('sat', 'sun') 
      and cal.dt not in (select day from T_PUBLIC_HOLIDAYS)
      
      

      代码如下:

      with 
      s_date as
      (
      select to_date('21-04-2020','dd-mm-yyyy') d from dual
      )
      , e_date as 
      (
      select to_date('01-05-2020','dd-mm-yyyy') d from dual
      )
      , no_of_days as 
      (
      select abs(trunc(s_date.d -  e_date.d))+1 no from s_date,e_date
      )
      , cal as
      (   
      select d+rownum-1 dt
      from s_date
      connect by level <= (select no from no_of_days) 
      )
      , cal2 as 
      (select 
          cal.dt 
          , rank() over (order by cal.dt) as day_order    
      from cal 
      where to_char(cal.dt, 'dy', 'nls_date_language=english') not in ('sat', 'sun') 
      and cal.dt not in (select day from T_PUBLIC_HOLIDAYS)
      )
      select dt from cal2 
      where day_order = 5 -- day_order = 6 if you want to exclude the start day
      

      【讨论】:

        【解决方案5】:

        这将在一个语句中计算多个开始日期和偏移日期的值:

        Oracle 设置:

        CREATE TABLE test_data ( start_date, days ) AS
        SELECT DATE '2019-01-01' + date_offset, days
        FROM   ( SELECT LEVEL - 1 AS date_offset FROM DUAL CONNECT BY LEVEL <= 7 )
               CROSS JOIN
               ( SELECT LEVEL - 1 AS days FROM DUAL CONNECT BY LEVEL <= 7 );
        
        CREATE TABLE holidays ( holiday_date ) AS
        SELECT DATE '2019-01-01' FROM DUAL UNION ALL
        SELECT DATE '2019-01-09' FROM DUAL UNION ALL
        SELECT DATE '2019-01-10' FROM DUAL;
        

        创建一个辅助函数来添加工作日。这不是必需的,因为您可以将函数展开到查询中,但它会使正在发生的事情更加明显:

        CREATE FUNCTION add_weekdays(
          start_date IN DATE,
          weekdays   IN NUMBER
        ) RETURN DATE DETERMINISTIC
        IS
        BEGIN
          RETURN start_date
                 -- add full weeks
                 + FLOOR( weekdays / 5 ) * 7
                 -- check to see if the remaining days go over a weekend, if so add 2 extra days
                 + CASE WHEN ( start_date - TRUNC( start_date, 'IW' ) ) + MOD( weekdays, 5 ) >= 5
                   THEN MOD( weekdays, 5 ) + 2
                   ELSE MOD( weekdays, 5 )
                   END;
        END;
        /
        

        查询:

        WITH offsets ( start_date, days, offset_date, holiday_days ) AS (
          SELECT start_date,
                 days,
                 add_weekdays( start_date, days ),
                 ( SELECT COUNT(*)
                   FROM   holidays
                   WHERE  holiday_date BETWEEN start_date
                                       AND add_weekdays( start_date, days )
                 )
          FROM   test_data
        UNION ALL
          -- recurse and add the previously found holiday days and look to see if any more holidays overlap
          SELECT start_date,
                 days,
                 add_weekdays( offset_date, holiday_days ),
                 ( SELECT COUNT(*)
                   FROM   holidays
                   WHERE  holiday_date BETWEEN offset_date + 1
                                       AND add_weekdays( offset_date, holiday_days )
                 )
          FROM   offsets
          -- stop recursing when no holiday days are found
          WHERE  holiday_days > 0
        )
        CYCLE start_date, days, offset_date SET is_cycle TO 1 DEFAULT 0
        SELECT TO_CHAR( start_date, 'DY YYYY-MM-DD' ) AS start_dt,
               days,
               TO_CHAR( offset_date, 'DY YYYY-MM-DD' ) AS offset_dt,
               ( offset_date - start_date ) - days AS skipped_days
        FROM   offsets
        WHERE  holiday_days = 0
        ORDER BY start_date, days;
        

        输出:

        (测试的节假日日期为2019-01-01、2019-01-09 和2019-01-10,跳过的天数包括节假日和周末)

        START_DT |天 | OFFSET_DT | SKIPPED_DAYS :------------- | ---: | :------------- | ------------: 周二 2019-01-01 | 0 |周三 2019-01-02 | 1 周二 2019-01-01 | 1 |星期四 2019-01-03 | 1 周二 2019-01-01 | 2 |周五 2019-01-04 | 1 周二 2019-01-01 | 3 |星期一 2019-01-07 | 3 周二 2019-01-01 | 4 |周二 2019-01-08 | 3 周二 2019-01-01 | 5 |周五 2019-01-11 | 5 周二 2019-01-01 | 6 |星期一 2019-01-14 | 7 周三 2019-01-02 | 0 |周三 2019-01-02 | 0 周三 2019-01-02 | 1 |星期四 2019-01-03 | 0 周三 2019-01-02 | 2 |周五 2019-01-04 | 0 周三 2019-01-02 | 3 |星期一 2019-01-07 | 2 周三 2019-01-02 | 4 |周二 2019-01-08 | 2 周三 2019-01-02 | 5 |周五 2019-01-11 | 4 周三 2019-01-02 | 6 |星期一 2019-01-14 | 6 星期四 2019-01-03 | 0 |星期四 2019-01-03 | 0 星期四 2019-01-03 | 1 |周五 2019-01-04 | 0 星期四 2019-01-03 | 2 |星期一 2019-01-07 | 2 星期四 2019-01-03 | 3 |周二 2019-01-08 | 2 星期四 2019-01-03 | 4 |周五 2019-01-11 | 4 星期四 2019-01-03 | 5 |星期一 2019-01-14 | 6 星期四 2019-01-03 | 6 |周二 2019-01-15 | 6 周五 2019-01-04 | 0 |周五 2019-01-04 | 0 周五 2019-01-04 | 1 |星期一 2019-01-07 | 2 周五 2019-01-04 | 2 |周二 2019-01-08 | 2 周五 2019-01-04 | 3 |周五 2019-01-11 | 4 周五 2019-01-04 | 4 |星期一 2019-01-14 | 6 周五 2019-01-04 | 5 |周二 2019-01-15 | 6 周五 2019-01-04 | 6 |周三 2019-01-16 | 6 SAT 2019-01-05 | 0 |星期一 2019-01-07 | 2 SAT 2019-01-05 | 1 |周二 2019-01-08 | 2 SAT 2019-01-05 | 2 |周五 2019-01-11 | 4 SAT 2019-01-05 | 3 |星期一 2019-01-14 | 6 SAT 2019-01-05 | 4 |周二 2019-01-15 | 6 SAT 2019-01-05 | 5 |周三 2019-01-16 | 6 SAT 2019-01-05 | 6 |周四 2019-01-17 | 6 孙 2019-01-06 | 0 |周二 2019-01-08 | 2 孙 2019-01-06 | 1 |周五 2019-01-11 | 4 孙 2019-01-06 | 2 |星期一 2019-01-14 | 6 孙 2019-01-06 | 3 |周二 2019-01-15 | 6 孙 2019-01-06 | 4 |周三 2019-01-16 | 6 孙 2019-01-06 | 5 |周四 2019-01-17 | 6 孙 2019-01-06 | 6 |周五 2019-01-18 | 6 星期一 2019-01-07 | 0 |星期一 2019-01-07 | 0 星期一 2019-01-07 | 1 |周二 2019-01-08 | 0 星期一 2019-01-07 | 2 |周五 2019-01-11 | 2 星期一 2019-01-07 | 3 |星期一 2019-01-14 | 4 星期一 2019-01-07 | 4 |周二 2019-01-15 | 4 星期一 2019-01-07 | 5 |周三 2019-01-16 | 4 星期一 2019-01-07 | 6 |周四 2019-01-17 | 4

        db小提琴here

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-07-25
          • 2017-07-26
          • 1970-01-01
          • 2020-07-19
          • 2018-12-24
          • 1970-01-01
          相关资源
          最近更新 更多