【问题标题】:How to exclude holidays between two dates?如何排除两个日期之间的假期?
【发布时间】:2013-06-18 08:07:16
【问题描述】:

我有两个日期,我必须找出这两个日期之间的星期日和节假日的数量。我可以使用 BETWEEN 做到这一点吗?如果有,怎么做?

SELECT date1, date2, trunc(deposit_date - transaction_date) TOTAL
FROM Table_Name FULL OUTER JOIN Holidays ON date2 = hdate
WHERE hdate IN (date1, date2)

使用它,我绝对可以检查这两天是否有假期,即 date1 或 date2,但我无法确定这两个日期之间是否有假期或星期日。救命!

【问题讨论】:

    标签: oracle date datetime oracle-sqldeveloper


    【解决方案1】:

    您发布的解决方案效率极低;您可以在一条 SQL 语句中完成所有这些操作:

    首先生成两者之间所有可能的日期:

     select trunc(:min_date) + level - 1
       from dual
    connect by level <= trunc(:min_date) - trunc(:max_date)
    

    然后使用您的 HOLIDAY 表来限制您想要的内容:

    with all_dates as (
     select trunc(:min_date) + level - 1 as the_date
       from dual
    connect by level <= trunc(:min_date) - trunc(:max_date)
            )
    select count(*)
      from all_dates a
      left outer join holiday b
        on a.the_date = b.hdate
     where b.hdate is null
       and to_char(a.the_date, 'DY') <> 'SUN'
    

    【讨论】:

    • 我发现了,我正要发布您刚刚发布的解决方案。我的解决方案的问题是它甚至还没有接近最佳解决方案,但它仍然有效,这是我当时所需要的。但是,现在我知道使用 LEFT OUTER JOIN 更容易完成这项工作。
    • 工作是任何解决方案@Monty :-) 的首要条件。我不会担心的!
    • 我现在明白了。感谢您提供更好的解决方案。
    【解决方案2】:

    如果你想检查hdate是否在两个日期之间,你可以使用

    where hdate between date1 and date2
    

    如果你想检查hdate是否与日期1或日期2在同一天,你可以这样查询

    where trunc(hdate) in (trunc(date1) ,trunc(date2))
    

    trunc 函数删除了时间。

    【讨论】:

    • 使用一些 LEFT JOINS 和一些运算符,我的一位高级同事解决了这个问题。我很快就会上传替代答案。
    【解决方案3】:

    您应该创建一个包含假期的表格并自行维护它。

    CREATE TABLE holidays
    (
        holiday VARCHAR2(100)
    ,   d_date DATE
    );
    
    INSERT INTO holidays VALUES ('National Developer Day', DATE'2013-06-01');
    
    SELECT  *
    FROM    holidays;
    -- National Developer Day   2013-06-01 00:00:00
    

    剩下的只是一条 SQL 语句的问题

    场景 1:存在

    SELECT  COUNT
            (
                CASE
                    WHEN TRIM(TO_CHAR(d.start_date_level, 'DAY')) = 'SUNDAY'
                    OR  CASE
                            WHEN EXISTS (SELECT 1 FROM holidays h WHERE d.start_date_level = h.d_date)
                            THEN 1
                            ELSE NULL
                        END = 1
                    THEN    1
                    ELSE    NULL
                END
            ) AS holiday_check
    FROM
    (
            SELECT  start_date + (LEVEL - 1) AS start_date_level
            FROM
            (
                    SELECT  start_date, end_date, end_date - start_date AS diff_date
                    FROM
                    (
                            SELECT  TRUNC(ADD_MONTHS(SYSDATE, -2)) AS start_date
                            ,       TRUNC(SYSDATE)                 AS end_date
                            FROM    DUAL
                    )
            ) 
            CONNECT BY
                    LEVEL <= (diff_date + 1)
    ) d
    

    场景 2:左连接

    SELECT  COUNT
            (
                CASE
                    WHEN TRIM(TO_CHAR(d.start_date_level, 'DAY')) = 'SUNDAY'
                    OR   h.d_date IS NOT NULL
                    THEN 1
                    ELSE NULL
                END
            ) AS holiday_check
    FROM
    (
            SELECT  start_date + (LEVEL - 1) AS start_date_level
            FROM
            (
                    SELECT  start_date, end_date, end_date - start_date AS diff_date
                    FROM
                    (
                            SELECT  TRUNC(ADD_MONTHS(SYSDATE, -2)) AS start_date
                            ,       TRUNC(SYSDATE)                 AS end_date
                            FROM    DUAL
                    )
            ) 
            CONNECT BY
                    LEVEL <= (diff_date + 1)
    ) d
    LEFT    JOIN holidays h
    ON      d.start_date_level = h.d_date
    

    9 个星期日 + 1 个“全国开发者日”=10

    【讨论】:

      【解决方案4】:
      CREATE OR REPLACE FUNCTION workdays (dt1 DATE, dt2 DATE) RETURN NUMBER IS
      weekday_count NUMBER := 0;
      date1 DATE := dt1;
      date2 DATE := dt2;
      cur_dt date;
      holiday_count number;
      begin
      if date1 = date2 then
          return 0;
      end if;
      cur_dt := transaction_date;
      while cur_dt <= date2 loop
          if cur_dt = date2 then 
              null;
          else
              SELECT count(*) INTO holiday_count 
              FROM holiday
               WHERE hdate = cur_dt;
              IF holiday_count = 0 THEN
                  IF to_char(cur_dt,'DY') NOT IN ('SUN') THEN
                      weekday_count := weekday_count + 1;
                  END IF;
              END IF;
          END IF;
          cur_dt := cur_dt +1;
      END LOOP;
      RETURN weekday_count;
      END;
      

      然后我查询了我的数据库并得到了正确的结果。如果您对此有最佳解决方案,请发布。

      【讨论】:

      • @Ben 发布了一个更好的解决方案。看看那个。
      【解决方案5】:

      这里有一个更好更有效的解决方案,

      SELECT A.ID,
      COUNT(A.ID) AS COUNTED
      FROM tableA A
      LEFT JOIN TableB B
      ON A.tableB_id=B.id
      LEFT JOIN holiday C
      ON TRUNC(C.hdate) BETWEEN (TRUNC(a.date1) +1) AND TRUNC(B.date2)
      WHERE c.hdate IS NOT NULL
      GROUP BY A.ID;
      

      其中 TableA 包含 date1,tableB 包含 date2。假期包含假期和周日的列表。并且此查询从计数中排除了“date1”。

      结果逻辑

      trunc(date2) - trunc(date1) = x      
      x - result of the query
      

      【讨论】:

        【解决方案6】:

        用您的假期(HDATE 列)制作一张 T$HOLIDAYS 表。这些日期将被排除在给定期间内的工作日计算之外(sdate 是开始日期,edate 是期间的结束日期)。以下是计算给定时间段内的工作日(不包括节假日、周六和周日)的函数:

        CREATE OR REPLACE FUNCTION WorkingDays(sdate IN DATE,edate IN DATE) RETURN NUMBER IS
          days NUMBER;
        BEGIN
          WITH dates AS (SELECT sdate+LEVEL-1 AS d FROM DUAL CONNECT BY LEVEL<=edate-sdate+1)
          SELECT COUNT(*) INTO days
          FROM dates
         WHERE d NOT IN (SELECT hdate FROM t$holidays) --exclude holidays
           AND TO_CHAR(d,'D') NOT IN (6,7); --exclude saturdays + sundays
          RETURN days;
        END WorkingDays;
        /
        

        【讨论】:

          【解决方案7】:
          select sum(qq) from (
           select case when to_number(to_char((trunc(sysdate-10) + level - 1),'D'))<=5 then 1 else 0 end as qq
             from dual
          connect by level <= trunc(sysdate) - trunc(sysdate-10))
          

          【讨论】:

            猜你喜欢
            • 2022-11-24
            • 1970-01-01
            • 1970-01-01
            • 2019-01-04
            • 2021-04-10
            • 1970-01-01
            • 2017-01-28
            • 2014-01-14
            • 1970-01-01
            相关资源
            最近更新 更多