How to eliminate non-working hours in Oracle

前端 未结 2 426
攒了一身酷
攒了一身酷 2020-12-18 16:34

I have two columns of DateTime type. The first one stores the DateTime when a process it started, the other one stores the DateTime when that process is finished. I want to

相关标签:
2条回答
  • 2020-12-18 17:15

    If i understand correctly, you want to calculate the difference between the start and finish date excluding the time before 10 am and after 7 pm.

    Here's the sample query and sql fiddle.

    SELECT start_time,
           finish_time,
           interval_time,
           EXTRACT (HOUR FROM interval_time), --extract the hours,mins and seconds from the interval
           EXTRACT (MINUTE FROM interval_time),
           EXTRACT (SECOND FROM interval_time)
      FROM (SELECT start_time,
                   finish_time,
                   NUMTODSINTERVAL (
                        CASE
                           WHEN finish_time - TRUNC (finish_time) > (19 / 24) --if finish time is after 7pm
                           THEN
                              TRUNC (finish_time) + (19 / 24)      --set it to 7pm
                           ELSE
                              finish_time      --else set it to actual finish time
                        END
                      - CASE
                           WHEN start_time - TRUNC (start_time) < (10 / 24) --if start time is before 10 am
                           THEN
                              TRUNC (start_time) + (10 / 24)    --set it to 10 am.
                           ELSE
                              start_time    --else set it to the actual start time
                        END,
                      'day') --subtract the both and convert the resulting day to interval
                      interval_time
              FROM timings);
    

    What I have done is,

    • Check if the start time is before 10 am and finish time is after 7 pm. If so, set the time to 10 am and 7 pm.
    • Then subtract the dates and convert the resulting days to Interval Type.
    • Then extract the hours, mins and seconds from the Interval.

    Note: This query assumes that both dates fall on same day and both are not before 10am or after 7 pm.

    UPDATE: To exclude holidays, the query will become complicated. I suggest writing three functions and use these functions in the query.

    1st function:

    FUNCTION modify_start_time (p_in_dte DATE) RETURN DATE
    ----------------------------------
    IF p_in_dte - TRUNC (p_in_dte) < (10 / 24)
    THEN
       RETURN TRUNC (p_in_dte) + (10 / 24);
    ELSIF p_in_dte - TRUNC (p_in_dte) > (19 / 24)
    THEN
       RETURN TRUNC (p_in_dte) + 1 + (10 / 24);
    ELSE
       RETURN p_in_dte;
    END IF;
    

    If the start time is outside the work hours, modify the start time to next nearest start time.

    2nd function:

    FUNCTION modify_finish_time (p_in_dte DATE) RETURN DATE
    ----------------------------------
    IF p_in_dte - TRUNC (p_in_dte) > (19 / 24)
    THEN
       RETURN TRUNC (p_in_dte) + (19 / 24);
    ELSIF p_in_dte - TRUNC (p_in_dte) < (10 / 24)
    THEN
       RETURN TRUNC (p_in_dte) - 1 + (19 / 24);
    ELSE
       RETURN p_in_dte;
    END IF;
    

    If the finish time is outside the work hours, modify it to the previous nearest finish time.

    3rd function:

    FUNCTION get_days_to_exclude (p_in_start_date     DATE,
                                  p_in_finish_date    DATE) RETURN NUMBER
    --------------------------------------------------------
    WITH cte --get all days between start and finish date
         AS (    SELECT p_in_start_date + LEVEL - 1 dte
                   FROM DUAL
             CONNECT BY LEVEL <= p_in_finish_date + 1 - p_in_starT_date)
    SELECT COUNT (1) * 9 / 24    --mutiply the days with work hours in a day
      INTO l_num_holidays
      FROM cte
     WHERE    TO_CHAR (dte, 'dy') = 'sun'    --find the count of sundays
           OR dte IN     --fins the count of holidays, assuming leaves are stored in separate table
                 (SELECT leave_date  
                    FROM leaves
                   WHERE leave_date BETWEEN p_in_start_date
                                        AND p_in_finish_date);
    
    l_num_holidays :=
       l_num_holidays + ( (p_in_finish_date - p_in_start_date) * (15 / 24)); --also, if the dates span more than a day find the non working hours.
    
    RETURN l_num_holidays;
    

    This function finds the no of days to be excluded while calculating the duration.

    So, the final query should be something like this,

    SELECT start_time,
           finish_time,
           CASE
              WHEN work_duration < 0 THEN NUMTODSINTERVAL (0, 'day')
              ELSE NUMTODSINTERVAL (work_duration, 'day')
           END
      FROM (SELECT start_time, finish_time,
                   --modify_start_time (start_time), modify_finish_time (finish_time),
                     modify_finish_time (finish_time)
                   - modify_start_time (start_time)
                   - get_days_to_exclude (
                        TRUNC (modify_start_time (start_time)),
                        TRUNC (modify_finish_time (finish_time)))
                      work_duration
              FROM timings);
    

    If the duration is less than 0, ignore it by setting it to 0.

    0 讨论(0)
  • 2020-12-18 17:28

    You can take the hour difference between the two dates and reduce the amount of non-working hours times the days between the two days. consider this example :

    create table t1 (
        start_hour date , finish_hour date);
    
    insert into t1 values (
      to_date('01/01/2013 12:00:00','dd/mm/yyyy hh24:mi:ss'),
      to_date('01/01/2013 15:00:00','dd/mm/yyyy hh24:mi:ss'));
    
    insert into t1 values (
      to_date('01/01/2013 12:00:00','dd/mm/yyyy hh24:mi:ss'),
      to_date('02/01/2013 15:00:00','dd/mm/yyyy hh24:mi:ss'));
    
    insert into t1 values (
      to_date('01/01/2013 18:00:00','dd/mm/yyyy hh24:mi:ss'),
      to_date('03/01/2013 11:00:00','dd/mm/yyyy hh24:mi:ss'));
    
    
    with x as (
    select start_hour ,finish_hour , 
          (finish_hour - start_hour) * 24 hour_diff , 
           trunc(finish_hour) - trunc(start_hour) as day_diff
    from t1
    ) 
    select  start_hour , finish_hour , 
            hour_diff - 15 * day_diff as working_hours 
    from x;
    
    |                     START_HOUR |                    FINISH_HOUR | WORKING_HOURS |
    -----------------------------------------------------------------------------------
    | January, 01 2013 12:00:00+0000 | January, 01 2013 15:00:00+0000 |             3 |
    | January, 01 2013 12:00:00+0000 | January, 02 2013 15:00:00+0000 |            12 |
    | January, 01 2013 18:00:00+0000 | January, 03 2013 11:00:00+0000 |            11 |
    
    0 讨论(0)
提交回复
热议问题