Calculating passed service hours - sql

I'm trying to calculate the time that has passed since a service request has been logged (service time), based on service hours.
Start time is the time the ticket has been logged (date_logged), end time would be either the current time (for open tickets) or the date_closed for closed tickets.
Service hours vary depending on the department the ticket was assigned to, as listed in the example below (all the times and dates in my table have the data type datetime):
Ticket-Nr.
Department
date_logged
date_closed
start_mon
end_mon
start_tue
end_tue
start_wed
end_wed
start_trs
end_trs
start_fri
end_fri
start_sat
end_sat
1234567
A
06.01.21 11:30:52
01.01.2001 07:30
01.01.2001 16:45
01.01.2001 07:30
01.01.2001 16:45
01.01.2001 07:30
01.01.2001 16:45
01.01.2001 07:30
01.01.2001 16:45
01.01.2001 07:30
01.01.2001 13:00
8912345
B
13.01.21 09:14:16
01.01.2001 07:00
01.01.2001 16:30
01.01.2001 07:00
01.01.2001 16:30
01.01.2001 07:00
01.01.2001 16:30
01.01.2001 07:00
01.01.2001 16:30
01.01.2001 07:00
01.01.2001 16:30
01.01.2001 07:00
01.01.2001 15:00
6789012
C
14.01.21 10:48:01
14.01.21 11:40
01.01.2001 07:00
01.01.2001 16:30
01.01.2001 07:00
01.01.2001 16:30
01.01.2001 07:00
01.01.2001 16:30
01.01.2001 07:00
01.01.2001 16:30
01.01.2001 07:00
01.01.2001 16:30
01.01.2001 07:00
01.01.2001 15:00
3456789
D
15.01.2021 09:41:00
01.01.2001 08:00
01.01.2001 15:00
01.01.2001 08:00
01.01.2001 15:00
01.01.2001 08:00
01.01.2001 15:00
01.01.2001 08:00
01.01.2001 15:00
01.01.2001 08:00
01.01.2001 13:00
0123456
B
02.01.2021 13:12:00
...
...
...
...
...
The result should look something like this (minus the "current datetime" column, that's just for context):
Ticket-Nr.
department
date_logged
service time [hh:mm]
current datetime
date_closed
1234567
A
06.01.21 11:30:52
62:33
14.01.2021 12:03
8912345
B
13.01.21 09:14:16
12:19
14.01.2021 12:03
6789012
C
14.01.21 10:48:01
00:28
14.01.2021 12:03
14.01.21 11:40
...
...
...
...
...
...
Public holidays have to be included for department A, B and D.

You can directly calculate the hours (extending your previous question):
SELECT ticket_nr,
department,
date_logged,
current_datetime,
date_closed,
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:SS"
FROM (
SELECT t.ticket_nr,
t.department,
t.date_logged,
SYSDATE AS current_datetime,
t.date_closed,
ROUND(
(
-- Calculate the full weeks difference from the start of ISO weeks.
(
TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' )
- TRUNC( date_logged, 'IW' )
) * ( s.hours_mon
+ s.hours_tue
+ s.hours_wed
+ s.hours_thu
+ s.hours_fri
+ s.hours_sat
+ s.hours_sun ) / (7*24)
-- Add the hours for the full days for the final week.
+ DECODE(
TRUNC( COALESCE( date_closed, SYSDATE ) )
- TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' ),
0, 0.0,
1, s.hours_mon,
2, s.hours_mon + s.hours_tue,
3, s.hours_mon + s.hours_tue + s.hours_wed,
4, s.hours_mon + s.hours_tue + s.hours_wed + s.hours_thu,
5, s.hours_mon + s.hours_tue + s.hours_wed + s.hours_thu + s.hours_fri,
6, s.hours_mon + s.hours_tue + s.hours_wed + s.hours_thu + s.hours_fri + s.hours_sat
) / 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, s.hours_mon,
2, s.hours_mon + s.hours_tue,
3, s.hours_mon + s.hours_tue + s.hours_wed,
4, s.hours_mon + s.hours_tue + s.hours_wed + s.hours_thu,
5, s.hours_mon + s.hours_tue + s.hours_wed + s.hours_thu + s.hours_fri,
6, s.hours_mon + s.hours_tue + s.hours_wed + s.hours_thu + s.hours_fri + s.hours_sat
) / 24
-- Add the hours of the final day
+ COALESCE(
GREATEST(
LEAST(
COALESCE( date_closed, SYSDATE ),
TRUNC( COALESCE( date_closed, SYSDATE ) )
+ DECODE(
TRUNC( COALESCE( date_closed, SYSDATE ) )
- TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' ),
0, s.end_mon,
1, s.end_tue,
2, s.end_wed,
3, s.end_thu,
4, s.end_fri,
5, s.end_sat,
6, s.end_sun
)
)
-
(
TRUNC( COALESCE( date_closed, SYSDATE ) )
+ DECODE(
TRUNC( COALESCE( date_closed, SYSDATE ) )
- TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' ),
0, s.start_mon,
1, s.start_tue,
2, s.start_wed,
3, s.start_thu,
4, s.start_fri,
5, s.start_sat,
6, s.start_sun
)
),
0
) / 24,
0
)
-- Subtract the hours of the day before the range starts.
+ COALESCE(
GREATEST(
LEAST(
date_logged,
date_logged
+ DECODE(
TRUNC( COALESCE( date_closed, SYSDATE ) )
- TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' ),
0, s.end_mon,
1, s.end_tue,
2, s.end_wed,
3, s.end_thu,
4, s.end_fri,
5, s.end_sat,
6, s.end_sun
)
)
-
(
date_logged
+ DECODE(
TRUNC( COALESCE( date_closed, SYSDATE ) )
- TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' ),
0, s.start_mon,
1, s.start_tue,
2, s.start_wed,
3, s.start_thu,
4, s.start_fri,
5, s.start_sat,
6, s.start_sun
)
),
0
) / 24,
0
)
)
-- Multiply to give seconds rather than fractions of full days.
* 24 * 60 * 60
) AS service_time_seconds
FROM table_name t
INNER JOIN service_hours s
ON ( s.department = t.department )
);
Which, for the sample data:
CREATE TABLE table_name ( Ticket_Nr, department, date_logged, date_closed ) AS
SELECT 1234567, 'A', DATE '2021-01-06' + INTERVAL '11:30:52' HOUR TO SECOND, NULL FROM DUAL UNION ALL
SELECT 8912345, 'B', DATE '2021-01-13' + INTERVAL '09:14:16' HOUR TO SECOND, NULL FROM DUAL UNION ALL
SELECT 6789012, 'C', DATE '2021-01-14' + INTERVAL '10:48:28' HOUR TO SECOND, DATE '2021-01-21' + INTERVAL '11:40:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 1, 'D', DATE '2021-01-07' + INTERVAL '07:00:00' HOUR TO SECOND, DATE '2021-01-14' + INTERVAL '07:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'A', DATE '2021-01-07' + INTERVAL '07:00:00' HOUR TO SECOND, DATE '2021-01-08' + INTERVAL '07:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'A', DATE '2021-01-08' + INTERVAL '07:00:00' HOUR TO SECOND, DATE '2021-01-09' + INTERVAL '07:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 4, 'A', DATE '2021-01-09' + INTERVAL '07:00:00' HOUR TO SECOND, DATE '2021-01-10' + INTERVAL '07:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 5, 'B', DATE '2021-01-09' + INTERVAL '07:00:00' HOUR TO SECOND, DATE '2021-01-10' + INTERVAL '07:00:00' HOUR TO SECOND FROM DUAL;
CREATE TABLE service_hours (
Department VARCHAR2(5)
CONSTRAINT service_hours__department__pk PRIMARY KEY,
start_mon INTERVAL DAY TO SECOND,
end_mon INTERVAL DAY TO SECOND,
hours_mon NUMBER
GENERATED ALWAYS AS (
COALESCE(
( DATE '1970-01-01' + end_mon ) - ( DATE '1970-01-01' + start_mon ),
0
) * 24
),
start_tue INTERVAL DAY TO SECOND,
end_tue INTERVAL DAY TO SECOND,
hours_tue NUMBER
GENERATED ALWAYS AS (
COALESCE(
( DATE '1970-01-01' + end_tue ) - ( DATE '1970-01-01' + start_tue ),
0
) * 24
),
start_wed INTERVAL DAY TO SECOND,
end_wed INTERVAL DAY TO SECOND,
hours_wed NUMBER
GENERATED ALWAYS AS (
COALESCE(
( DATE '1970-01-01' + end_wed ) - ( DATE '1970-01-01' + start_wed ),
0
) * 24
),
start_thu INTERVAL DAY TO SECOND,
end_thu INTERVAL DAY TO SECOND,
hours_thu NUMBER
GENERATED ALWAYS AS (
COALESCE(
( DATE '1970-01-01' + end_thu ) - ( DATE '1970-01-01' + start_thu ),
0
) * 24
),
start_fri INTERVAL DAY TO SECOND,
end_fri INTERVAL DAY TO SECOND,
hours_fri NUMBER
GENERATED ALWAYS AS (
COALESCE(
( DATE '1970-01-01' + end_fri ) - ( DATE '1970-01-01' + start_fri ),
0
) * 24
),
start_sat INTERVAL DAY TO SECOND,
end_sat INTERVAL DAY TO SECOND,
hours_sat NUMBER
GENERATED ALWAYS AS (
COALESCE(
( DATE '1970-01-01' + end_sat ) - ( DATE '1970-01-01' + start_sat ),
0
) * 24
),
start_sun INTERVAL DAY TO SECOND,
end_sun INTERVAL DAY TO SECOND,
hours_sun NUMBER
GENERATED ALWAYS AS (
COALESCE(
( DATE '1970-01-01' + end_sun ) - ( DATE '1970-01-01' + start_sun ),
0
) * 24
)
);
INSERT INTO service_hours (
Department,
start_mon, end_mon,
start_tue, end_tue,
start_wed, end_wed,
start_thu, end_thu,
start_fri, end_fri,
start_sat, end_sat,
start_sun, end_sun
)
SELECT 'A',
INTERVAL '07:30' HOUR TO MINUTE, INTERVAL '16:45' HOUR TO MINUTE,
INTERVAL '07:30' HOUR TO MINUTE, INTERVAL '16:45' HOUR TO MINUTE,
INTERVAL '07:30' HOUR TO MINUTE, INTERVAL '16:45' HOUR TO MINUTE,
INTERVAL '07:30' HOUR TO MINUTE, INTERVAL '16:45' HOUR TO MINUTE,
INTERVAL '07:30' HOUR TO MINUTE, INTERVAL '13:00' HOUR TO MINUTE,
NULL, NULL,
NULL, NULL
FROM DUAL UNION ALL
SELECT 'B',
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '15:00' HOUR TO MINUTE,
NULL, NULL
FROM DUAL UNION ALL
SELECT 'C',
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '15:00' HOUR TO MINUTE,
NULL, NULL
FROM DUAL UNION ALL
SELECT 'D',
INTERVAL '08:00' HOUR TO MINUTE, INTERVAL '15:00' HOUR TO MINUTE,
INTERVAL '08:00' HOUR TO MINUTE, INTERVAL '15:00' HOUR TO MINUTE,
INTERVAL '08:00' HOUR TO MINUTE, INTERVAL '15:00' HOUR TO MINUTE,
INTERVAL '08:00' HOUR TO MINUTE, INTERVAL '15:00' HOUR TO MINUTE,
INTERVAL '08:00' HOUR TO MINUTE, INTERVAL '15:00' HOUR TO MINUTE,
NULL, NULL,
NULL, NULL
FROM DUAL;
Outputs:
TICKET_NR | DEPARTMENT | DATE_LOGGED | CURRENT_DATETIME | DATE_CLOSED | SERVICE_TIME HH:MM:SS
--------: | :--------- | :------------------------ | :------------------------ | :------------------------ | :--------------------
1234567 | A | 2021-01-06 11:30:52 (WED) | 2021-01-15 13:38:49 (FRI) | null | 61:13:45
8912345 | B | 2021-01-13 09:14:16 (WED) | 2021-01-15 13:38:49 (FRI) | null | 19:16:37
6789012 | C | 2021-01-14 10:48:28 (THU) | 2021-01-15 13:38:49 (FRI) | 2021-01-21 11:40:00 (THU) | 55:41:40
1 | D | 2021-01-07 07:00:00 (THU) | 2021-01-15 13:38:49 (FRI) | 2021-01-14 07:00:00 (THU) | 35:00:00
2 | A | 2021-01-07 07:00:00 (THU) | 2021-01-15 13:38:49 (FRI) | 2021-01-08 07:00:00 (FRI) | 9:15:00
3 | A | 2021-01-08 07:00:00 (FRI) | 2021-01-15 13:38:49 (FRI) | 2021-01-09 07:00:00 (SAT) | 5:30:00
4 | A | 2021-01-09 07:00:00 (SAT) | 2021-01-15 13:38:49 (FRI) | 2021-01-10 07:00:00 (SUN) | 0:00:00
5 | B | 2021-01-09 07:00:00 (SAT) | 2021-01-15 13:38:49 (FRI) | 2021-01-10 07:00:00 (SUN) | 8:00:00
db<>fiddle here

Related

Split row data base on timestamp SQL Oracle

Good day everyone. I have a table as below. Duration is the time from current state to next state.
Timestamp
State
Duration(minutes)
10/9/2022 8:50:00 AM
A
35
10/9/2022 9:25:00 AM
B
10
10/9/2022 9:35:00 AM
C
...
How do I split data at 9:00 AM of each day like below:
Timestamp
State
Duration(minutes)
10/9/2022 8:50:00 AM
A
10
10/9/2022 9:00:00 AM
A
25
10/9/2022 9:25:00 AM
B
10
10/9/2022 9:35:00 AM
C
...
Thank you.
Use a row-generator function to generate extra rows when the timestamp is before 09:00 and the next timestamp is after 09:00 (and calculate the diff value rather than storing it in the table):
SELECT l.ts AS timestamp,
t.state,
ROUND((l.next_ts - l.ts) * 24 * 60, 2) As diff
FROM (
SELECT timestamp,
LEAD(timestamp) OVER (ORDER BY timestamp) AS next_timestamp,
state
FROM table_name
) t
CROSS APPLY (
SELECT GREATEST(
t.timestamp,
TRUNC(t.timestamp - INTERVAL '9' HOUR) + INTERVAL '9' HOUR + LEVEL - 1
) AS ts,
LEAST(
t.next_timestamp,
TRUNC(t.timestamp - INTERVAL '9' HOUR) + INTERVAL '9' HOUR + LEVEL
) AS next_ts
FROM DUAL
CONNECT BY
TRUNC(t.timestamp - INTERVAL '9' HOUR) + INTERVAL '9' HOUR + LEVEL - 1 < t.next_timestamp
) l;
Which, for your sample data:
CREATE TABLE table_name (Timestamp, State) AS
SELECT DATE '2022-10-09' + INTERVAL '08:50' HOUR TO MINUTE, 'A' FROM DUAL UNION ALL
SELECT DATE '2022-10-09' + INTERVAL '09:25' HOUR TO MINUTE, 'B' FROM DUAL UNION ALL
SELECT DATE '2022-10-09' + INTERVAL '09:35' HOUR TO MINUTE, 'C' FROM DUAL UNION ALL
SELECT DATE '2022-10-12' + INTERVAL '09:35' HOUR TO MINUTE, 'D' FROM DUAL;
Outputs:
TIMESTAMP
STATE
DIFF
2022-10-09 08:50:00
A
10
2022-10-09 09:00:00
A
25
2022-10-09 09:25:00
B
10
2022-10-09 09:35:00
C
1405
2022-10-10 09:00:00
C
1440
2022-10-11 09:00:00
C
1440
2022-10-12 09:00:00
C
35
2022-10-12 09:35:00
D
null
fiddle

find number of hours gap when no one is working

i have a table view of table
taking scenario of a hotel that works 24/7 non stop, i want to calculate total number of hours in a month when no one is present .
in image you can see we have shift hours in 24hh format, shifts are 8 hours long and different employees may have different week offs , we have date range for which this shift would be valid , i.e. a typical month and we have planned leaves when employee is completely off.
can you suggest oracle SQL to find total number of hours when no employee is working in a month.
You can generate the shifts and then find the shifts where no-one worked:
WITH shifts (shift_start) AS (
SELECT DATE '2022-07-01' + INTERVAL '6' HOUR + INTERVAL '8' HOUR * (LEVEL - 1)
FROM DUAL
CONNECT BY
DATE '2022-07-01' + INTERVAL '6' HOUR + INTERVAL '8' HOUR * (LEVEL - 1)
< DATE '2022-08-01'
)
SELECT s.shift_start
FROM shifts s
WHERE NOT EXISTS(
SELECT 1
FROM work_master w
WHERE w.shift_date_frm <= s.shift_start
AND s.shift_start < w.shift_date_to + INTERVAL '1' DAY
AND ':' || w.week_off_day || ':' NOT LIKE '%:' || TO_CHAR(s.shift_start, 'Dy') || ':%'
AND EXTRACT(HOUR FROM CAST(s.shift_start AS TIMESTAMP)) = w.shift_start
AND (
(
w.vac_date_frm IS NULL
AND w.vac_date_to IS NULL
)
OR NOT (
w.vac_date_frm <= s.shift_start
AND s.shift_start < w.vac_date_to + INTERVAL '1' DAY
)
)
)
Which, for the sample data:
CREATE TABLE work_master (
employee_name,
shift_start,
shift_end,
shift_date_frm,
shift_date_to,
vac_date_frm,
vac_date_to,
week_off_day
) AS
SELECT 'emp1', 22, 6, DATE '2022-07-01', DATE '2022-07-31', NULL, NULL, 'Sat:Sun' FROM DUAL UNION ALL
SELECT 'emp2', 14, 22, DATE '2022-07-01', DATE '2022-07-31', NULL, NULL, 'Sat:Sun' FROM DUAL UNION ALL
SELECT 'emp3', 6, 14, DATE '2022-07-01', DATE '2022-07-31', DATE '2022-07-27', DATE '2022-07-27', 'Sat:Sun' FROM DUAL UNION ALL
SELECT 'emp4', 14, 22, DATE '2022-07-01', DATE '2022-07-31', NULL, NULL, 'Fri:Sat' FROM DUAL UNION ALL
SELECT 'emp5', 22, 6, DATE '2022-07-01', DATE '2022-07-31', NULL, NULL, 'Wed:Thu' FROM DUAL;
Outputs:
SHIFT_START
2022-07-02 06:00:00 (Sat)
2022-07-02 14:00:00 (Sat)
2022-07-03 06:00:00 (Sun)
2022-07-09 06:00:00 (Sat)
2022-07-09 14:00:00 (Sat)
2022-07-10 06:00:00 (Sun)
2022-07-16 06:00:00 (Sat)
2022-07-16 14:00:00 (Sat)
2022-07-17 06:00:00 (Sun)
2022-07-23 06:00:00 (Sat)
2022-07-23 14:00:00 (Sat)
2022-07-24 06:00:00 (Sun)
2022-07-27 06:00:00 (Wed)
2022-07-30 06:00:00 (Sat)
2022-07-30 14:00:00 (Sat)
2022-07-31 06:00:00 (Sun)
If you just want the total hours then:
WITH shifts (shift_start) AS (
SELECT DATE '2022-07-01' + INTERVAL '6' HOUR + INTERVAL '8' HOUR * (LEVEL - 1)
FROM DUAL
CONNECT BY
DATE '2022-07-01' + INTERVAL '6' HOUR + INTERVAL '8' HOUR * (LEVEL - 1)
< DATE '2022-08-01'
)
SELECT COUNT(*) * 8 AS hours_not_worked
FROM shifts s
WHERE NOT EXISTS(
SELECT 1
FROM work_master w
WHERE w.shift_date_frm <= s.shift_start
AND s.shift_start < w.shift_date_to + INTERVAL '1' DAY
AND ':' || w.week_off_day || ':' NOT LIKE '%:' || TO_CHAR(s.shift_start, 'Dy') || ':%'
AND EXTRACT(HOUR FROM CAST(s.shift_start AS TIMESTAMP)) = w.shift_start
AND (
(
w.vac_date_frm IS NULL
AND w.vac_date_to IS NULL
)
OR NOT (
w.vac_date_frm <= s.shift_start
AND s.shift_start < w.vac_date_to + INTERVAL '1' DAY
)
)
)
Which outputs:
HOURS_NOT_WORKED
128
db<>fiddle here

Calculating passed service hours for open tickets (Oracle SQL)

I'm trying to calculate the time that has passed since a service request has been logged (service time), based on service hours.
Start time is the time the ticket has been logged (date_logged), end time would be either the current time for open tickets or the date_closed for closed tickets.
Service hours are:
Monday to Thursday 07:00 - 16:30
Friday 07:00 - 13:00
So it should look something like this (minus the "current datetime" column, that's just for context):
Ticket-Nr.
date_logged
service time [hh:mm]
current datetime
date_closed
1234567
06.01.21 11:30:52
62:33
14.01.2021 12:03
8912345
13.01.21 09:14:16
12:19
14.01.2021 12:03
6789012
14.01.21 10:48:01
00:28
14.01.2021 12:03
14.01.21 11:40
...
...
...
...
...
I can't say yet whether public holidays have to be included or not, so we can ignore those for now.
I'm thankful for any help whatsoever!
You can calculate the amount of time (adapted from my answers here and here):
SELECT ticket_nr,
date_logged,
current_datetime,
date_closed,
TO_CHAR( FLOOR( service_time_seconds / 60 / 60 ), 'FM9990' )
|| ':'
|| TO_CHAR( MOD( FLOOR( service_time_seconds / 60 ), 60 ), 'FM00' )
|| ':'
|| TO_CHAR( MOD( service_time_seconds, 60 ), 'FM00' )
AS "SERVICE_TIME HH:MM:SS"
FROM (
SELECT ticket_nr,
date_logged,
SYSDATE AS current_datetime,
date_closed,
ROUND(
(
-- Calculate the full weeks difference from the start of ISO weeks.
(
TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' )
- TRUNC( date_logged, 'IW' )
) * (9.5*4+6)/(7*24)
-- Add the hours for the full days for the final week.
+ DECODE(
TRUNC( COALESCE( date_closed, SYSDATE ) )
- TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' ),
0, 0.0,
1, 9.5,
2, 19.0,
3, 28.5,
4, 38.0,
44.0
) / 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, 9.5,
2, 19.0,
3, 28.5,
4, 38.0,
44.0
) / 24
-- Add the hours of the final day
+ LEAST(
GREATEST(
COALESCE( date_closed, SYSDATE )
- ( TRUNC( COALESCE( date_closed, SYSDATE ) )
+ INTERVAL '07:00' HOUR TO MINUTE
),
0
),
DECODE(
TRUNC( COALESCE( date_closed, SYSDATE ) )
- TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' ),
0, 9.5,
1, 9.5,
2, 9.5,
3, 9.5,
4, 6.0,
0.0
) / 24
)
-- Subtract the hours of the day before the range starts.
- LEAST(
GREATEST(
date_logged
- ( TRUNC( date_logged ) + INTERVAL '07:00' HOUR TO MINUTE ),
0
),
DECODE(
TRUNC( date_logged )
- TRUNC( date_logged, 'IW' ),
0, 9.5,
1, 9.5,
2, 9.5,
3, 9.5,
4, 6.0,
0.0
) / 24
)
)
-- Multiply to give seconds rather than fractions of full days.
* 24 * 60 * 60
) AS service_time_seconds
FROM table_name
);
Which, for the sample data:
CREATE TABLE table_name ( Ticket_Nr, date_logged, date_closed ) AS
SELECT 1234567, DATE '2021-01-06' + INTERVAL '11:30:52' HOUR TO SECOND, NULL FROM DUAL UNION ALL
SELECT 8912345, DATE '2021-01-13' + INTERVAL '09:14:16' HOUR TO SECOND, NULL FROM DUAL UNION ALL
SELECT 6789012, DATE '2021-01-14' + INTERVAL '10:48:28' HOUR TO SECOND, DATE '2021-01-21' + INTERVAL '11:40:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 1, DATE '2021-01-07' + INTERVAL '07:00:00' HOUR TO SECOND, DATE '2021-01-14' + INTERVAL '07:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, DATE '2021-01-07' + INTERVAL '07:00:00' HOUR TO SECOND, DATE '2021-01-08' + INTERVAL '07:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, DATE '2021-01-08' + INTERVAL '07:00:00' HOUR TO SECOND, DATE '2021-01-09' + INTERVAL '07:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 4, DATE '2021-01-09' + INTERVAL '07:00:00' HOUR TO SECOND, DATE '2021-01-10' + INTERVAL '07:00:00' HOUR TO SECOND FROM DUAL
Outputs (where NLS_DATE_FORMAT is YYYY-MM-DD HH24:MI:SS (DY)):
TICKET_NR | DATE_LOGGED | CURRENT_DATETIME | DATE_CLOSED | SERVICE_TIME HH:MM:SS
--------: | :------------------------ | :------------------------ | :------------------------ | :--------------------
1234567 | 2021-01-06 11:30:52 (WED) | 2021-01-14 12:36:54 (THU) | null | 54:36:02
8912345 | 2021-01-13 09:14:16 (WED) | 2021-01-14 12:36:54 (THU) | null | 12:52:38
6789012 | 2021-01-14 10:48:28 (THU) | 2021-01-14 12:36:54 (THU) | 2021-01-21 11:40:00 (THU) | 44:51:32
1 | 2021-01-07 07:00:00 (THU) | 2021-01-14 12:36:54 (THU) | 2021-01-14 07:00:00 (THU) | 44:00:00
2 | 2021-01-07 07:00:00 (THU) | 2021-01-14 12:36:54 (THU) | 2021-01-08 07:00:00 (FRI) | 9:30:00
3 | 2021-01-08 07:00:00 (FRI) | 2021-01-14 12:36:54 (THU) | 2021-01-09 07:00:00 (SAT) | 6:00:00
4 | 2021-01-09 07:00:00 (SAT) | 2021-01-14 12:36:54 (THU) | 2021-01-10 07:00:00 (SUN) | 0:00:00
db<>fiddle here

Multiple row to multiple column based on data in SQL Oracle

output will be as image
SQL Oracle query Sample
select * from (
select * from (
with fifteen as (select trunc(sysdate) + (level * 15)/(24*60) c_time from dual connect by level <= (24*60) / 15 )
select to_number(to_char(c_time, 'hh')) HR,to_char(c_time, 'hh24:mi')||' - '||to_char(c_time+ 15 / (24 * 60), 'hh24:mi') TimeSlots
from fifteen
where extract(hour from cast (c_time as timestamp)) between 2 and 5
) t
)
Oracle Query 1:
with fifteen as (
select CAST( TRUNC( sysdate ) AS TIMESTAMP ) + level * INTERVAL '15' MINUTE c_time,
MOD( LEVEL,4 ) AS quarter
from dual
connect by level <= (24*60) / 15
)
SELECT "2", "3", "4", "5"
FROM (
select EXTRACT( HOUR FROM c_time ) HR,
quarter,
to_char(c_time, 'hh24:mi')||' - '||to_char(c_time + INTERVAL '15' MINUTE, 'hh24:mi') TimeSlots
from fifteen
) t
PIVOT( MAX( timeslots ) FOR HR IN ( 2, 3, 4, 5 ) )
ORDER BY quarter
Oracle Query 2:
SELECT TO_CHAR(two, 'HH24:MI') || ' - ' || TO_CHAR(two +INTERVAL '15' MINUTE,'HH24:MI') AS "2",
TO_CHAR(three,'HH24:MI') || ' - ' || TO_CHAR(three+INTERVAL '15' MINUTE,'HH24:MI') AS "3",
TO_CHAR(four ,'HH24:MI') || ' - ' || TO_CHAR(four +INTERVAL '15' MINUTE,'HH24:MI') AS "4",
TO_CHAR(five ,'HH24:MI') || ' - ' || TO_CHAR(five +INTERVAL '15' MINUTE,'HH24:MI') AS "5"
FROM (
SELECT DATE '1970-01-01' + INTERVAL '2' HOUR + INTERVAL '15' MINUTE * (LEVEL - 1) AS two,
DATE '1970-01-01' + INTERVAL '3' HOUR + INTERVAL '15' MINUTE * (LEVEL - 1) AS three,
DATE '1970-01-01' + INTERVAL '4' HOUR + INTERVAL '15' MINUTE * (LEVEL - 1) AS four,
DATE '1970-01-01' + INTERVAL '5' HOUR + INTERVAL '15' MINUTE * (LEVEL - 1) AS five
FROM DUAL
CONNECT BY LEVEL <= 4
)
Output:
2 3 4 5
------------- ------------- ------------- -------------
02:00 - 02:15 03:00 - 03:15 04:00 - 04:15 05:00 - 05:15
02:15 - 02:30 03:15 - 03:30 04:15 - 04:30 05:15 - 05:30
02:30 - 02:45 03:30 - 03:45 04:30 - 04:45 05:30 - 05:45
02:45 - 03:00 03:45 - 04:00 04:45 - 05:00 05:45 - 06:00

Calculate time diffrence in SQL with shifts

i have two table
the first table contains the record of a ticket with start date and end date
start_date | End_Date
21-02-2017 07:52:32 | 22-02-2017 09:56:32
21-02-2017 09:52:32 | 23-02-2017 17:52:32
the second table contains the details of the weekly shift:
shift_day | Start_Time | End_Time
MON 9:00 18:00
TUE 10:00 19:00
WED 9:00 18:00
THU 10:00 19:00
FRI 9:00 18:00
I am looking to get the time difference in the first table which will only include the time as per the second table.
Use a recursive sub-query factoring clause to generate each day within your time ranges and then correlate that with your shifts to restrict the time for each day to be within the shift hours and then aggregate to get the total:
Oracle 18 Setup:
CREATE TABLE times ( start_date, End_Date ) AS
SELECT DATE '2017-02-21' + INTERVAL '07:52:32' HOUR TO SECOND,
DATE '2017-02-22' + INTERVAL '09:56:32' HOUR TO SECOND
FROM DUAL
UNION ALL
SELECT DATE '2017-02-21' + INTERVAL '09:52:32' HOUR TO SECOND,
DATE '2017-02-23' + INTERVAL '17:52:32' HOUR TO SECOND
FROM DUAL;
CREATE TABLE weekly_shifts ( shift_day, Start_Time, End_Time ) AS
SELECT 'MON', INTERVAL '09:00' HOUR TO MINUTE, INTERVAL '18:00' HOUR TO MINUTE FROM DUAL UNION ALL
SELECT 'TUE', INTERVAL '10:00' HOUR TO MINUTE, INTERVAL '19:00' HOUR TO MINUTE FROM DUAL UNION ALL
SELECT 'WED', INTERVAL '09:00' HOUR TO MINUTE, INTERVAL '18:00' HOUR TO MINUTE FROM DUAL UNION ALL
SELECT 'THU', INTERVAL '10:00' HOUR TO MINUTE, INTERVAL '19:00' HOUR TO MINUTE FROM DUAL UNION ALL
SELECT 'FRI', INTERVAL '09:00' HOUR TO MINUTE, INTERVAL '18:00' HOUR TO MINUTE FROM DUAL;
Query 1:
WITH days ( id, start_date, day_start, day_end, end_date ) AS (
SELECT ROWNUM,
start_date,
start_date,
LEAST( TRUNC( start_date ) + INTERVAL '1' DAY, end_date ),
end_date
FROM times
UNION ALL
SELECT id,
start_date,
day_end,
LEAST( day_end + INTERVAL '1' DAY, end_date ),
end_date
FROM days
WHERE day_end < end_date
)
SELECT start_date,
end_date,
SUM( shift_end - shift_start ) AS days_worked_on_shift
FROM (
SELECT ID,
start_date,
end_date,
GREATEST( day_start, TRUNC( day_start ) + start_time ) AS shift_start,
LEAST( day_end, TRUNC( day_start ) + end_time ) AS shift_end
FROM days d
INNER JOIN
weekly_shifts w
ON ( TO_CHAR( d.day_start, 'DY' ) = w.shift_day )
)
GROUP BY id, start_date, end_date;
Result:
START_DATE END_DATE DAYS_WORKED_ON_SHIFT
------------------- ------------------- --------------------
2017-02-21 07:52:32 2017-02-22 09:56:32 0.414259259259259259
2017-02-21 09:52:32 2017-02-23 17:52:32 1.078148148148148148