Oracle SQL 时差(小时)

Oracle SQL Time Difference in hours

我正在尝试找到一种在比较两个日期时获取假期时间的方法。

下面的查询目前不包括假期和 returns 工作时间。我正在努力以另一种方式得到它。只是 return 开始日期和结束日期之间的假期小时数。

如果开始日期是 15-04-22 16:00 并且是假期,那么查询应该只有 return 小时 b/w 16:00 - 18:00. (工作时间在 7:00 - 18:00 之间)


create table holidays_tb(
holiday_date date
);

insert into holidays_tb values (TO_DATE('15/04/2022', 'DD/MM/YYYY')); 
insert into holidays_tb values (TO_DATE('18/04/2022', 'DD/MM/YYYY'));


declare
v_st_date date;
v_end_date date;
return_val number;
begin
v_st_date := TO_DATE('13/04/2022 09:55:52', 'DD/MM/YYYY HH24:MI:SS'); 
v_end_date := TO_DATE('19/04/2022 16:30:00', 'DD/MM/YYYY HH24:MI:SS'); 
with all_days as
            (select trunc(v_st_date) + level - 1 as a_dt
            from dual
              connect by level <= 1 + (v_end_date - v_st_date)
            minus
            select holiday_date from holidays_tb
            )
          select sum (11)
          into return_val
          from all_days
          where TO_CHAR ( a_dt , 'Dy') NOT IN ('Sat', 'Sun');
dbms_output.put_line(  return_val );        
end;

现在已经坚持了几个多小时了:|

您可以使用以下方式获取假期小时数:

DECLARE
  v_st_date  date := DATE '2022-04-13' + INTERVAL '0 09:55:52' DAY TO SECOND;
  v_end_date date := DATE '2022-04-19' + INTERVAL '0 16:30:00' DAY TO SECOND;
  v_work_day_start INTERVAL DAY(0) TO SECOND(0) := INTERVAL '0 07:00:00' DAY TO SECOND;
  v_work_day_end   INTERVAL DAY(0) TO SECOND(0) := INTERVAL '0 18:00:00' DAY TO SECOND;
  v_hours NUMBER := EXTRACT(HOUR FROM v_work_day_end - v_work_day_start)
                  + EXTRACT(MINUTE FROM v_work_day_end - v_work_day_start)/60
                  + EXTRACT(MINUTE FROM v_work_day_end - v_work_day_start)/3600;
  v_holiday NUMBER;
  return_val number;
BEGIN
  SELECT SUM(
           LEAST(holiday_date + v_work_day_end, v_end_date)
           - GREATEST(holiday_date + v_work_day_start, v_st_date)
         ) * 24
  INTO   v_holiday
  FROM   holidays_tb
  WHERE  holiday_date BETWEEN TRUNC(v_st_date) AND v_end_date
  AND    holiday_date - TRUNC(holiday_date, 'IW') < 5;

  DBMS_OUTPUT.PUT_LINE( v_holiday );
END;
/

其中,对于示例数据,输出 22


此外,您不需要使用递归查询来获取天数,可以直接使用以下方法计算:

DECLARE
  v_st_date  date := DATE '2022-04-13' + INTERVAL '0 09:55:52' DAY TO SECOND;
  v_end_date date := DATE '2022-04-19' + INTERVAL '0 16:30:00' DAY TO SECOND;
  v_work_day_start INTERVAL DAY(0) TO SECOND(0) := INTERVAL '0 07:00:00' DAY TO SECOND;
  v_work_day_end   INTERVAL DAY(0) TO SECOND(0) := INTERVAL '0 18:00:00' DAY TO SECOND;
  v_hours NUMBER := EXTRACT(HOUR FROM v_work_day_end - v_work_day_start)
                  + EXTRACT(MINUTE FROM v_work_day_end - v_work_day_start)/60
                  + EXTRACT(MINUTE FROM v_work_day_end - v_work_day_start)/3600;
  v_holiday NUMBER;
  return_val number;
BEGIN
  SELECT SUM(
           LEAST(holiday_date + v_work_day_end, v_end_date)
           - GREATEST(holiday_date + v_work_day_start, v_st_date)
         ) * 24
  INTO   v_holiday
  FROM   holidays_tb
  WHERE  holiday_date BETWEEN TRUNC(v_st_date) AND v_end_date
  AND    holiday_date - TRUNC(holiday_date, 'IW') < 5;

  return_val :=
    -- Full weeks
    (TRUNC(v_end_date, 'IW') - TRUNC(v_st_date, 'IW')) * 5 / 7 * v_hours
    -- Full days before in start week
    - LEAST(TRUNC(v_st_date) - TRUNC(v_st_date, 'IW'), 5) * v_hours
    -- Part days before in start week
    - CASE
      WHEN v_st_date - TRUNC(v_st_date, 'IW') < 5
      THEN LEAST(
             GREATEST(
               (v_st_date - (TRUNC(v_st_date) + v_work_day_start))* 24,
               0
             ),
             v_hours
           )
      ELSE 0
      END
    -- End full days
    + LEAST(TRUNC(v_end_date) - TRUNC(v_end_date, 'IW'), 5) * v_hours
    -- End part days
    + CASE
      WHEN v_end_date - TRUNC(v_end_date, 'IW') < 5
      THEN LEAST(
             GREATEST(
               (v_end_date - (TRUNC(v_end_date) + v_work_day_start))* 24,
               0
             ),
             v_hours
           )
      ELSE 0
      END
    -- Holiday hours
    - v_holiday;
    
  DBMS_OUTPUT.PUT_LINE( return_val );        
END;
/

输出:28.5689(3 天 33 小时,结束时减去 1.5 小时,开始时将近 3 小时)。