简体   繁体   中英

Oracle SQL Time Difference in hours

I'm trying to find a way to get the holiday hours while comparing two dates.

the query below at the moment excludes the holidays and returns the working hours. I'm struggling to get it the other way. Just to return number of hours of holidays which falls between start and end date.

If the start date is of 15-04-22 16:00 and its a holiday then query should only return hours b/w 16:00 - 18:00. (work hours would be between 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;

Have been stuck at it for more than couple of hours now:|

You can get the number of holiday hours using:

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;
/

Which, for the sample data, outputs 22 .


Additionally, you do not need to use a recursive query to get the number of days and can directly calculate it using:

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;
/

Which outputs: 28.5689 (33 hours for 3 full days less 1.5 hours at the end and almost 3 hours at the start).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM