简体   繁体   中英

Calculating processing time based on business hours instead of total hours

I'm trying to calculate the service hours that have passed since a ticket has been logged. When a ticket gets logged a timestamp is saved with it ( date_logged ). When it gets closed another timestamp is saved ( date_closed ).

Needed

What I need is the hours that have passed between the date_logged and the current datetime (for open tickets) or the hours between the date_logged and the date_closed (for closed tickets) based on the service hours of the department assigned to the ticket.

Public holidays have to be included.

Existing Table

The service hours of the assigned department are saved in the same table as the ticket. The table looks something like this:

incident_ref department date_logged date_closed sla_mon_start sla_mon_end sla_tue_start sla_tue_end sla_wed_start sla_wed_end sla_thr_start sla_thr_end sla_fri_start sla_fri_end sla_sat_start sla_sat_end sla_sun_start sla_sun_end
1660565 A 06.01.21 11:30:52 01.01.01 07:30:00 01.01.01 16:45:00 01.01.01 07:30:00 01.01.01 16:45 01.01.01 07:30:00 01.01.01 16:45:00 01.01.01 07:30:00 01.01.01 16:45:00 01.01.01 07:30:00 01.01.01 13:00:00 01.01.01 00:00:00 01.01.01 00:00:00 01.01.01 00:00:00 01.01.01 00:00:00
1660567 B 13.01.21 09:14:16 01.01.01 07:00:00 01.01.01 16:30:00 01.01.01 07:00:00 01.01.01 16:30:00 01.01.01 07:00:00 01.01.01 16:30:00 01.01.01 07:00:00 01.01.01 16:30:00 01.01.01 07:00:00 01.01.01 16:30:00 01.01.01 07:00:00 01.01.01 15:00:00 01.01.01 00:00:00 01.01.01 00:00:00
1660558 C 31.12.20 07:04:46 31.12.20 07:36:59 01.01.01 07:00:00 01.01.01 16:30:00 01.01.01 07:00:00 01.01.01 16:30:00 01.01.01 07:00:00 01.01.01 16:30:00 01.01.01 07:00:00 01.01.01 16:30:00 01.01.01 07:00:00 01.01.01 16:30:00 01.01.01 07:00:00 01.01.01 15:00:00 01.01.01 00:00:00 01.01.01 00:00:00
3456789 D 01.01.21 09:41:00 04.01.21 08:21:00 01.01.01 08:00:00 01.01.01 15:00:00 01.01.01 08:00:00 01.01.01 15:00:00 01.01.01 08:00:00 01.01.01 15:00:00 01.01.01 08:00:00 01.01.01 15:00:00 01.01.01 08:00:00 01.01.01 13:00:00 01.01.01 00:00:00 01.01.01 00:00:00 01.01.01 00:00:00 01.01.01 00:00:00
0123456 D 02.01.21 13:12:00 ... ... ... ... ...

I only have the permission to read on this database, so I'm not allowed to alter existing tables or create new ones.

What I've tried

Based on an answer I got on a previous question of mine, I tried to solve it like shown in the following, but this way I only get the error "date+date not allowed". I haven't tried to include public holidays yet, because the rest already didn't work.

SELECT incident_ref,
       date_logged,
       Inc_close_date,
       TO_CHAR( FLOOR( service_time_seconds / 60 / 60 ), 'FM99990' )
       || ':'
       || TO_CHAR( MOD( FLOOR( service_time_seconds / 60 ), 60 ), 'FM00' )
       || ':'
       || TO_CHAR( MOD( service_time_seconds, 60 ), 'FM00' )
         AS "service time [hh:mm]"
FROM   (
SELECT inc.incident_ref,
       inc.date_logged,
       inc.Inc_close_date,
       ROUND(
         (
           -- Calculate the full weeks difference from the start of ISO weeks.
           ( 
             TRUNC( COALESCE( Inc_close_date, SYSDATE ), 'IW' )
             - TRUNC( date_logged, 'IW' )
           ) * ( (24*(sla_mon_end-sla_mon_start))
                 + (24*(sla_tue_end-sla_tue_start))
                 + (24*(sla_wed_end-sla_wed_start))
                 + (24*(sla_thr_end-sla_thr_start))
                 + (24*(sla_fri_end-sla_fri_start))
                 + (24*(sla_sat_end-sla_sat_start))
                 + (24*(sla_sun_end-sla_sun_start) )) / (7*24)
           -- Add the hours for the full days for the final week.
           + DECODE(
               TRUNC( COALESCE( Inc_close_date, SYSDATE ) )
               - TRUNC( COALESCE( Inc_close_date, SYSDATE ), 'IW' ),
               0,  0.0,
               1, (24*(sla_mon_end-sla_mon_start)),
               2, (24*(sla_mon_end-sla_mon_start)) + 24*(sla_wed_end-sla_tue_start),
               3, (24*(sla_mon_end-sla_mon_start)) + 24*(sla_wed_end-sla_tue_start) + 24*(sla_wed_end-sla_wed_start),
               4, (24*(sla_mon_end-sla_mon_start)) + 24*(sla_wed_end-sla_tue_start) + 24*(sla_wed_end-sla_wed_start) + 24*(sla_thr_end-sla_thr_start),
               5, (24*(sla_mon_end-sla_mon_start)) + 24*(sla_wed_end-sla_tue_start) + 24*(sla_wed_end-sla_wed_start) + 24*(sla_thr_end-sla_thr_start) + 24*(sla_fri_end-sla_fri_start),
               6, (24*(sla_mon_end-sla_mon_start)) + 24*(sla_wed_end-sla_tue_start) + 24*(sla_wed_end-sla_wed_start) + 24*(sla_thr_end-sla_thr_start) + 24*(sla_fri_end-sla_fri_start) + 24*(sla_sat_end-sla_sat_start)
             ) / 24
           -- Subtract the hours for the full days from the days of the week
           -- before the date logged.
           - DECODE(
               TRUNC( date_logged ) - TRUNC( date_logged, 'IW' ),
               0,  0.0,
              1, 24*(sla_mon_end-sla_mon_start),
               2, 24*(sla_mon_end-sla_mon_start) + 24*(sla_wed_end-sla_tue_start),
               3, 24*(sla_mon_end-sla_mon_start) + 24*(sla_wed_end-sla_tue_start) + 24*(sla_wed_end-sla_wed_start),
               4, 24*(sla_mon_end-sla_mon_start) + 24*(sla_wed_end-sla_tue_start) + 24*(sla_wed_end-sla_wed_start) + 24*(sla_thr_end-sla_thr_start),
               5, 24*(sla_mon_end-sla_mon_start) + 24*(sla_wed_end-sla_tue_start) + 24*(sla_wed_end-sla_wed_start) + 24*(sla_thr_end-sla_thr_start) + 24*(sla_fri_end-sla_fri_start),
               6, 24*(sla_mon_end-sla_mon_start) + 24*(sla_wed_end-sla_tue_start) + 24*(sla_wed_end-sla_wed_start) + 24*(sla_thr_end-sla_thr_start) + 24*(sla_fri_end-sla_fri_start) + 24*(sla_sat_end-sla_sat_start)
             ) / 24
           -- Add the hours of the final day
           + COALESCE(
               GREATEST(
                 LEAST(
                   COALESCE( Inc_close_date, SYSDATE ),
                   TRUNC( COALESCE( Inc_close_date, SYSDATE ) )
                   + DECODE(
                       TRUNC( COALESCE( Inc_close_date, SYSDATE ) )
                       - TRUNC( COALESCE( Inc_close_date, SYSDATE ), 'IW' ),
                       0, sla_mon_end,
                       1, sla_tue_end,
                       2, sla_wed_end,
                       3, sla_thr_end,
                       4, sla_fri_end,
                       5, sla_sat_end,
                       6, sla_sun_end
                     )
                 )
                 -
                 (
                   TRUNC( COALESCE( Inc_close_date, SYSDATE ) )
                   + DECODE(
                       TRUNC( COALESCE( Inc_close_date, SYSDATE ) )
                       - TRUNC( COALESCE( Inc_close_date, SYSDATE ), 'IW' ),
                       0, sla_mon_start,
                       1, sla_tue_start,
                       2, sla_wed_start,
                       3, sla_thr_start,
                       4, sla_fri_start,
                       5, sla_sat_start,
                       6, sla_sun_start
                     )
                   ),
                 0
               ) / 24,
               0
             )
           -- Subtract the hours of the day before the range starts.
           + COALESCE(
               GREATEST(
                 LEAST(
                   date_logged,
                   date_logged
                   + DECODE(
                       TRUNC( COALESCE( Inc_close_date, SYSDATE ) )
                       - TRUNC( COALESCE( Inc_close_date, SYSDATE ), 'IW' ),
                       0, sla_mon_end,
                       1, sla_tue_end,
                       2, sla_wed_end,
                       3, sla_thr_end,
                       4, sla_fri_end,
                       5, sla_sat_end,
                       6, sla_sun_end
                     )
                 )
                 -
                 (
                   date_logged
                   + DECODE(
                       TRUNC( COALESCE( Inc_close_date, SYSDATE ) )
                       - TRUNC( COALESCE( Inc_close_date, SYSDATE ), 'IW' ),
                       0, sla_mon_start,
                       1, sla_tue_start,
                       2, sla_wed_start,
                       3, sla_thr_start,
                       4, sla_fri_start,
                       5, sla_sat_start,
                       6, sla_sun_start
                     )
                   ),
                 0
               ) / 24,
               0
             )
         )
         -- Multiply to give seconds rather than fractions of full days.
         * 24 * 60 * 60
       ) AS service_time_seconds
FROM   incident inc
);

Expected results

(The tickets are the same ones as in the table above, if you should wonder about the departments. The current datetime column is just for reference, it isn't needed in the actual result)

incident_ref date_logged date_closed service time [hh:mm] current datetime
1660565 31.12.20 07:15:48 131:54 22.01.2021 10:09
1660567 31.12.20 07:17:56 160:21 22.01.2021 10:09
1660558 31.12.20 07:04:46 31.12.20 07:36:59 00:32 22.01.2021 10:09
3456789 01.01.21 09:41:00 04.01.21 08:21:00 00:21 22.01.2021 10:09

With the last ticket you can see that the assigned department didn't work on new year.

Maybe this would help:

WITH
    sample AS       -- sample data with DATE_CLOSED defined
        (
            SELECT DISTINCT
                INCIDENT_REF,
                DEPARTMENT, SLA_MON_START, SLA_MON_END,  SLA_TUE_START, SLA_TUE_END,  SLA_WED_START, SLA_WED_END,  SLA_THR_START, SLA_THR_END,  SLA_FRI_START, SLA_FRI_END,  SLA_SAT_START, SLA_SAT_END,  SLA_SUN_START, SLA_SUN_END,
                DATE_LOGGED,
                To_Char(DATE_LOGGED, 'hh:mi') "TIME_LOGGED",
                Nvl(DATE_CLOSED, To_Date('01.22.2021 10:09', 'mm.dd.yyyy hh24:mi')) "DATE_CLOSED", 
                To_Char(Nvl(DATE_CLOSED, To_Date('01.22.2021 10:09', 'mm.dd.yyyy hh24:mi')), 'hh:mi') "TIME_CLOSED"
            FROM
                SAMPLEDATA s
        ),
    days AS         -- generate all the missing dates with the service hours depending on a day of week - calculate effective service time (hours) by date
        (
            SELECT DISTINCT 
                INCIDENT_REF "INCIDENT_REF",
                DEPARTMENT "DEPARTMENT",
                DATE_LOGGED "DATE_LOGGED",
                DATE_LOGGED + LEVEL - 1 "WRK_DATE",
                CASE
                    WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '1' THEN To_Char(SLA_MON_START, 'hh24:mi') || '-' || To_Char(SLA_MON_END, 'hh24:mi')  
                    WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '2' THEN To_Char(SLA_TUE_START, 'hh24:mi') || '-' || To_Char(SLA_TUE_END, 'hh24:mi') 
                    WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '3' THEN To_Char(SLA_WED_START, 'hh24:mi') || '-' || To_Char(SLA_WED_END, 'hh24:mi') 
                    WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '4' THEN To_Char(SLA_THR_START, 'hh24:mi') || '-' || To_Char(SLA_THR_END, 'hh24:mi') 
                    WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '5' THEN To_Char(SLA_FRI_START, 'hh24:mi') || '-' || To_Char(SLA_FRI_END, 'hh24:mi') 
                    WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '6' THEN To_Char(SLA_SAT_START, 'hh24:mi') || '-' || To_Char(SLA_SAT_END, 'hh24:mi') 
                    WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '7' THEN To_Char(SLA_SUN_START, 'hh24:mi') || '-' || To_Char(SLA_SUN_END, 'hh24:mi') 
                END "SERVICE_HOURS_SPAN",
                --
               CASE
                    WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '1' THEN (SLA_MON_END - SLA_MON_START) * 24  
                    WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '2' THEN (SLA_TUE_END - SLA_TUE_START) * 24  
                    WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '3' THEN (SLA_WED_END - SLA_WED_START) * 24  
                    WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '4' THEN (SLA_THR_END - SLA_THR_START) * 24  
                    WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '5' THEN (SLA_FRI_END - SLA_FRI_START) * 24  
                    WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '6' THEN (SLA_SAT_END - SLA_SAT_START) * 24  
                    WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '7' THEN (SLA_SUN_END - SLA_SUN_START) * 24  
                END "SCHEDULED_SERVICE_HOURS",
              --
               CASE
                    WHEN TRUNC(DATE_LOGGED, 'dd') = TRUNC(DATE_LOGGED  + LEVEL - 1, 'dd') And To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '1' And TRUNC(DATE_LOGGED, 'dd') <> TRUNC(Nvl(DATE_CLOSED, To_Date('01.22.2021', 'mm.dd.yyyy')), 'dd') THEN (SLA_MON_END - To_Date(To_Char(SLA_MON_END, 'mm.dd.yyyy') || ' ' || TIME_LOGGED, 'mm.dd.yyyy hh24:mi')) * 24  
                    WHEN TRUNC(DATE_LOGGED, 'dd') = TRUNC(DATE_LOGGED  + LEVEL - 1, 'dd') And To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '2' And TRUNC(DATE_LOGGED, 'dd') <> TRUNC(Nvl(DATE_CLOSED, To_Date('01.22.2021', 'mm.dd.yyyy')), 'dd') THEN (SLA_TUE_END - To_Date(To_Char(SLA_TUE_END, 'mm.dd.yyyy') || ' ' || TIME_LOGGED, 'mm.dd.yyyy hh24:mi')) * 24  
                    WHEN TRUNC(DATE_LOGGED, 'dd') = TRUNC(DATE_LOGGED  + LEVEL - 1, 'dd') And To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '3' And TRUNC(DATE_LOGGED, 'dd') <> TRUNC(Nvl(DATE_CLOSED, To_Date('01.22.2021', 'mm.dd.yyyy')), 'dd') THEN (SLA_WED_END - To_Date(To_Char(SLA_WED_END, 'mm.dd.yyyy') || ' ' || TIME_LOGGED, 'mm.dd.yyyy hh24:mi')) * 24  
                    WHEN TRUNC(DATE_LOGGED, 'dd') = TRUNC(DATE_LOGGED  + LEVEL - 1, 'dd') And To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '4' And TRUNC(DATE_LOGGED, 'dd') <> TRUNC(Nvl(DATE_CLOSED, To_Date('01.22.2021', 'mm.dd.yyyy')), 'dd') THEN (SLA_THR_END - To_Date(To_Char(SLA_THR_END, 'mm.dd.yyyy') || ' ' || TIME_LOGGED, 'mm.dd.yyyy hh24:mi')) * 24  
                    WHEN TRUNC(DATE_LOGGED, 'dd') = TRUNC(DATE_LOGGED  + LEVEL - 1, 'dd') And To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '5' And TRUNC(DATE_LOGGED, 'dd') <> TRUNC(Nvl(DATE_CLOSED, To_Date('01.22.2021', 'mm.dd.yyyy')), 'dd') THEN (SLA_FRI_END - To_Date(To_Char(SLA_FRI_END, 'mm.dd.yyyy') || ' ' || TIME_LOGGED, 'mm.dd.yyyy hh24:mi')) * 24  
                    WHEN TRUNC(DATE_LOGGED, 'dd') = TRUNC(DATE_LOGGED  + LEVEL - 1, 'dd') And To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '6' And TRUNC(DATE_LOGGED, 'dd') <> TRUNC(Nvl(DATE_CLOSED, To_Date('01.22.2021', 'mm.dd.yyyy')), 'dd') THEN (SLA_SAT_END - To_Date(To_Char(SLA_SAT_END, 'mm.dd.yyyy') || ' ' || TIME_LOGGED, 'mm.dd.yyyy hh24:mi')) * 24  
                    WHEN TRUNC(DATE_LOGGED, 'dd') = TRUNC(DATE_LOGGED  + LEVEL - 1, 'dd') And To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '7' And TRUNC(DATE_LOGGED, 'dd') <> TRUNC(Nvl(DATE_CLOSED, To_Date('01.22.2021', 'mm.dd.yyyy')), 'dd') THEN (SLA_SUN_END - To_Date(To_Char(SLA_SUN_END, 'mm.dd.yyyy') || ' ' || TIME_LOGGED, 'mm.dd.yyyy hh24:mi')) * 24  
                  --
                    WHEN TRUNC(DATE_LOGGED, 'dd') = TRUNC(Nvl(DATE_CLOSED, To_Date('01.22.2021', 'mm.dd.yyyy')), 'dd') THEN (DATE_CLOSED - DATE_LOGGED) * 24
              ELSE
                    CASE
                        WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '1' THEN (SLA_MON_END - SLA_MON_START) * 24  
                        WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '2' THEN (SLA_TUE_END - SLA_TUE_START) * 24  
                        WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '3' THEN (SLA_WED_END - SLA_WED_START) * 24  
                        WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '4' THEN (SLA_THR_END - SLA_THR_START) * 24  
                        WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '5' THEN (SLA_FRI_END - SLA_FRI_START) * 24  
                        WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '6' THEN (SLA_SAT_END - SLA_SAT_START) * 24  
                        WHEN To_Char(DATE_LOGGED  + LEVEL - 1, 'd') = '7' THEN (SLA_SUN_END - SLA_SUN_START) * 24  
                    END    
              END "EFF_SERVICE_HOURS",
              --
                DATE_CLOSED "DATE_CLOSED"
            FROM
                sample
        WHERE
            TRUNC(DATE_LOGGED, 'dd') <= TRUNC(Nvl(DATE_CLOSED, To_Date('01.22.2021 10:09', 'mm.dd.yyyy hh:mi')), 'dd')
            CONNECT BY
                DATE_LOGGED + LEVEL - 1 <= DATE_CLOSED
            ORDER BY 
                DEPARTMENT, DATE_LOGGED + LEVEL - 1
        )       
    
SELECT              -- Get total service time as hh24:mi
    INCIDENT_REF "INCIDENT_REF",
    DEPARTMENT "DEPARTMENT",
    To_Char(DATE_LOGGED, 'MON-dd-yyyy hh24:mi') "DATE_LOGGED",
    To_Char(DATE_CLOSED, 'MON-dd-yyyy hh24:mi') "DATE_CLOSED",
    LPAD(FLOOR(Sum(EFF_SERVICE_HOURS * 60) / 60), 2, '0') || ':' || LPAD(Round((Sum(EFF_SERVICE_HOURS) - FLOOR(Sum(EFF_SERVICE_HOURS * 60) / 60)) * 60, 0), 2, '0') "TOTAL_SERVICE_TIME"
FROM
    days
GROUP BY 
    INCIDENT_REF,
    DEPARTMENT,
    DATE_LOGGED,
    DATE_CLOSED
ORDER BY 
    DEPARTMENT,
  INCIDENT_REF

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