Multiple row to multiple column based on data in SQL Oracle - sql

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

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

Group by Hour for Epoch time

I need to group by Hour for converted epoch time, here is what I have done but it is still grouping by the day. Can anyone support?
select count (*) as "Total",to_char(trunc(to_date('19700101 00', 'YYYYMMDD HH24') + ( 1 / 24 / 60 / 60 ) * INSERTION_TIME),'DD/MM/YYYY HH24') as "Day"
from Records
where ID=35 and (INSERTION_TIME/86400)+ to_date('01-01-1970 00:00:00','dd-mm-yyyy HH24:MI:SS') >=sysdate - 4
group by trunc(to_date('19700101 00', 'YYYYMMDD HH24') + ( 1 / 24 / 60 / 60 ) * INSERTION_TIME)
order by trunc(to_date('19700101 00', 'YYYYMMDD HH24') + ( 1 / 24 / 60 / 60 ) * INSERTION_TIME)
You can get rid of most of the arithmetic and just use:
SELECT COUNT(*) as "Total",
TO_CHAR(
TRUNC(DATE '1970-01-01' + INTERVAL '1' SECOND * INSERTION_TIME, 'HH'),
'DD/MM/YYYY HH24'
) as "Day"
FROM Records
WHERE ID=35
AND DATE '1970-01-01' + INTERVAL '1' SECOND * INSERTION_TIME >=sysdate - 4
GROUP BY
TRUNC(DATE '1970-01-01' + INTERVAL '1' SECOND * INSERTION_TIME, 'HH')
ORDER BY
TRUNC(DATE '1970-01-01' + INTERVAL '1' SECOND * INSERTION_TIME, 'HH')
Note: Epoch time is usually 1970-01-01 00:00:00 UTC. If your session time zone is not UTC then you may be calculating the epoch time incorrectly and would want to use timestamps instead:
SELECT COUNT(*) as "Total",
TO_CHAR(
TRUNC(
TIMESTAMP '1970-01-01 00:00:00 UTC' + INTERVAL '1' SECOND * INSERTION_TIME,
'HH'
),
'DD/MM/YYYY HH24'
) as "Day"
FROM Records
WHERE ID=35
AND TIMESTAMP '1970-01-01 00:00:00 UTC' + INTERVAL '1' SECOND * INSERTION_TIME
>=SYSTIMESTAMP - INTERVAL '4' DAY
GROUP BY
TRUNC(
TIMESTAMP '1970-01-01 00:00:00 UTC' + INTERVAL '1' SECOND * INSERTION_TIME,
'HH'
)
ORDER BY
TRUNC(
TIMESTAMP '1970-01-01 00:00:00 UTC' + INTERVAL '1' SECOND * INSERTION_TIME,
'HH'
)
Notice the behavior of trunc on your expression. It removes the hour component.
Test case with dbfiddle.uk:
Test case
The ungrouped SQL, with extra expression in select list:
select to_char(trunc(to_date('19700101 00', 'YYYYMMDD HH24') + ( 1 / 24 / 60 / 60 ) * INSERTION_TIME),'DD/MM/YYYY HH24') as "Day"
, to_char( (to_date('19700101 00', 'YYYYMMDD HH24') + ( 1 / 24 / 60 / 60 ) * INSERTION_TIME),'DD/MM/YYYY HH24') as "Day"
from Records
where ID = 35 and (INSERTION_TIME/86400) + to_date('01-01-1970 00:00:00','dd-mm-yyyy HH24:MI:SS') >= sysdate - 4
AND rownum < 6
order by (to_date('19700101 00', 'YYYYMMDD HH24') + ( 1 / 24 / 60 / 60 ) * INSERTION_TIME)
;
The ungrouped result:
Now add the GROUP BY:
select COUNT(*) AS n
, to_char(trunc(to_date('19700101 00', 'YYYYMMDD HH24') + ( 1 / 24 / 60 / 60 ) * INSERTION_TIME),'DD/MM/YYYY HH24') as "Day"
, to_char( (to_date('19700101 00', 'YYYYMMDD HH24') + ( 1 / 24 / 60 / 60 ) * INSERTION_TIME),'DD/MM/YYYY HH24') as "Day"
from Records
where ID = 35 and (INSERTION_TIME/86400) + to_date('01-01-1970 00:00:00','dd-mm-yyyy HH24:MI:SS') >= sysdate - 4
group by (to_date('19700101 00', 'YYYYMMDD HH24') + ( 1 / 24 / 60 / 60 ) * INSERTION_TIME)
order by (to_date('19700101 00', 'YYYYMMDD HH24') + ( 1 / 24 / 60 / 60 ) * INSERTION_TIME)
;
The grouped result:
To create a test case:
CREATE TABLE Records (
id int
, insertion_time int
);
INSERT INTO Records VALUES (35, 4011134567);
INSERT INTO Records VALUES (35, 4011134567);
INSERT INTO Records VALUES (35, 4011134567);
INSERT INTO Records VALUES (35, 4011134567);

Calculating passed service hours

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

Oracle query Round up or down to nearest 15 minute interval

08-SEP-20 08:55:05
08-SEP-20 15:36:13
The query below is working correctly for 15:36:13 in that it rounds to 15:30 but the 8:55:05 is rounding down to 08:45 when it should be rounding to 09:00
select event_date,trunc(event_date,'mi') - numtodsinterval( mod(to_char(event_date,'mi'),15), 'minute' ) as nearest_quarter
from time_source_in_all where empno = '002307718' and event_date between '8-sep-2020' and '9-sep-2020'
I think this will do what you want:
select trunc(event_date, 'HH') + round(extract(minute from event_date) / 15) * interval '15' minute )
. . .
Note: I prefer the extract() because it is standard SQL. But it assumes that the column is a timestamp and not a date.
or the equivalent:
select trunc(event_date, 'HH') + numtodsinterval(round(to_number(to_char(event_date, 'MI')) / 15) * 15, 'minute')
You can use:
SELECT event_date,
TRUNC( event_date, 'HH' )
+ ROUND( EXTRACT( MINUTE FROM CAST( event_date AS TIMESTAMP ) ) / 15 )
* INTERVAL '15' MINUTE
AS rounded_15_event_date
FROM table_name
or:
SELECT event_date,
TRUNC( event_date, 'HH' )
+ ROUND( ( event_date - TRUNC( event_date, 'HH' ) ) * 24 * 4 )
* INTERVAL '15' MINUTE
AS rounded_15_event_date
FROM table_name
Which, for your sample data:
CREATE TABLE table_name ( event_date ) AS
SELECT DATE '2020-09-08' + INTERVAL '08:55:05' HOUR TO SECOND FROM DUAL UNION ALL
SELECT DATE '2020-09-08' + INTERVAL '15:36:13' HOUR TO SECOND FROM DUAL
Both output:
EVENT_DATE | ROUNDED_15_EVENT_DATE
:------------------ | :--------------------
2020-09-08 08:55:05 | 2020-09-08 09:00:00
2020-09-08 15:36:13 | 2020-09-08 15:30:00
db<>fiddle here