Oracle, Split a Time Duration Row by One Hour Period

Oracle, split a time duration row by one hour period

I have bulit a basic query, you can work around it and get what you want.

select greatest(Start_time, trunc(Start_time+(level-1)/24, 'hh24')), 
least(End_time, trunc(Start_time+(level)/24, 'hh24'))
from log_table
connect by level <= floor((dt1-dt2)*24)+1;

Example at sqlfiddle:

http://sqlfiddle.com/#!4/82625/29

Oracle, split a time duration multiple rows by one hour period

You're really very close. Add a DISTINCT and an ORDER BY and I think you've got the results you were looking for:

SELECT DISTINCT AdmitDate,
PatientName,
Room,
greatest(start_time, trunc(start_time+(level-1)/24, 'hh24')) beginTime,
least(end_time, trunc(start_time+(level)/24, 'hh24')) endTime
from Utilization
connect by level <= floor((trunc(end_time, 'hh')-trunc(start_time, 'hh'))*24)+1
ORDER BY 1, 2, 3, 4, 5

This produces:

ADMITDATE               PATIENTNAME ROOM BEGINTIME              ENDTIME
2012-01-24 00:00:00 Patient1 RM1 2012-01-24 07:30:00 2012-01-24 08:00:00
2012-01-24 00:00:00 Patient1 RM1 2012-01-24 08:00:00 2012-01-24 08:32:00
2012-01-24 00:00:00 Patient2 RM1 2012-01-24 08:45:00 2012-01-24 09:00:00
2012-01-24 00:00:00 Patient2 RM1 2012-01-24 09:00:00 2012-01-24 09:13:00

SQLFiddle here

How do I split a duration of time into hourly intervals in Oracle SQL?

You can use a cte to recursively add an hour until the target is reached:

with cte(userid,date_capture,login_hour,logout_hour,target) as (
select userid,
date_capture,
login_hour,
login_hour + 1,
logout_hour
from table_name
union all
select userid,
date_capture,
logout_hour,
logout_hour + 1,
target
from cte
where logout_hour + 1 <= target
)
select userid,
date_capture,
login_hour,
logout_hour
from cte
order by userid,
date_capture,
login_hour;

How can i split a row of data at an hour level?

I'm not sure how happy 8i will be with this, but you can split the records up into hour (or partial hour) chunks with a hierarchical query like this:

select firstoccurrence, lastoccurrence, tally,
greatest(firstoccurrence,
trunc(firstoccurrence, 'HH24') + (level - 1) / 24) as new_start,
least(lastoccurrence,
trunc(firstoccurrence, 'HH24') + level / 24) as new_end
from t42
connect by firstoccurrence = prior firstoccurrence
and prior sys_guid() is not null
and trunc(firstoccurrence, 'HH24') + (level - 1) / 24
<= trunc(lastoccurrence, 'HH24');

... where t42 is a table populated as in your first image. That gives:

FIRSTOCCURRENCE     LASTOCCURRENCE      TALLY NEW_START           NEW_END           
------------------- ------------------- ----- ------------------- -------------------
2014-12-04 11:33:16 2014-12-04 11:33:36 1 2014-12-04 11:33:16 2014-12-04 11:33:36
2014-12-30 11:41:46 2014-12-30 16:23:08 7 2014-12-30 11:41:46 2014-12-30 12:00:00
2014-12-30 11:41:46 2014-12-30 16:23:08 7 2014-12-30 12:00:00 2014-12-30 13:00:00
2014-12-30 11:41:46 2014-12-30 16:23:08 7 2014-12-30 13:00:00 2014-12-30 14:00:00
2014-12-30 11:41:46 2014-12-30 16:23:08 7 2014-12-30 14:00:00 2014-12-30 15:00:00
2014-12-30 11:41:46 2014-12-30 16:23:08 7 2014-12-30 15:00:00 2014-12-30 16:00:00
2014-12-30 11:41:46 2014-12-30 16:23:08 7 2014-12-30 16:00:00 2014-12-30 16:23:08

You can then use that as a CTE, or as I don't think 8i knows about CTEs an inline view, and calculate the difference between each time and it's proportion against the original range, and divide the tally by that:

select firstoccurrence, lastoccurrence, tally, new_start, new_end,
to_number(to_char(new_start, 'HH24')) as new_hour,
tally / ((lastoccurrence - firstoccurrence) / (new_end - new_start)) as new_tally
from (
select firstoccurrence, lastoccurrence, tally,
greatest(firstoccurrence,
trunc(firstoccurrence, 'HH24') + (level - 1) / 24) as new_start,
least(lastoccurrence,
trunc(firstoccurrence, 'HH24') + level / 24) as new_end
from t42
connect by firstoccurrence = prior firstoccurrence
and prior sys_guid() is not null
and trunc(firstoccurrence, 'HH24') + (level - 1) / 24
<= trunc(lastoccurrence, 'HH24')
);

FIRSTOCCURRENCE LASTOCCURRENCE TALLY NEW_START NEW_END NEW_HOUR NEW_TALLY
------------------- ------------------- ----- ------------------- ------------------- -------- -----------
2014-12-04 11:33:16 2014-12-04 11:33:36 1 2014-12-04 11:33:16 2014-12-04 11:33:36 11 1.000000
2014-12-30 11:41:46 2014-12-30 16:23:08 7 2014-12-30 11:41:46 2014-12-30 12:00:00 11 .453619
2014-12-30 11:41:46 2014-12-30 16:23:08 7 2014-12-30 12:00:00 2014-12-30 13:00:00 12 1.492714
2014-12-30 11:41:46 2014-12-30 16:23:08 7 2014-12-30 13:00:00 2014-12-30 14:00:00 13 1.492714
2014-12-30 11:41:46 2014-12-30 16:23:08 7 2014-12-30 14:00:00 2014-12-30 15:00:00 14 1.492714
2014-12-30 11:41:46 2014-12-30 16:23:08 7 2014-12-30 15:00:00 2014-12-30 16:00:00 15 1.492714
2014-12-30 11:41:46 2014-12-30 16:23:08 7 2014-12-30 16:00:00 2014-12-30 16:23:08 16 .575524

SQL Fiddle demo; but that's 11gR2 so doesn't mean it'll work in 8i. I don't have such an ancient version to test on unfortunately.

If your table has an ID column then use that for the connect by prior clause - I've had to use firstoccurrence and hope it's unique. You may also need to use dbms_random.value instead of sys_guid() - an non-deterministic function will do though.

Split Time for each Hour in a day given start and end time

You can use generate series for calculating the interval in seconds,minutes,hours


CREATE TABLE userlogs
(
usernm varchar(300),
state varchar(300),
start_time timestamp,
end_time timestamp
);

INSERT INTO userlogs (usernm,state,start_time,end_time) VALUES ('user1', 'Work', '2022-08-15 11:00:38.000000 +00:00', '2022-08-15 14:11:03.000000 +00:00');
INSERT INTO userlogs (usernm,state,start_time,end_time) VALUES ('user1', 'Break', '2022-08-15 14:11:03.000000 +00:00', '2022-08-15 14:25:25.000000 +00:00');
INSERT INTO userlogs (usernm,state,start_time,end_time) VALUES ('user1', 'Work', '2022-08-15 14:25:25.000000 +00:00', '2022-08-15 15:09:10.000000 +00:00');
INSERT INTO userlogs (usernm,state,start_time,end_time) VALUES ('user1', 'Break', '2022-08-15 15:09:10.000000 +00:00', '2022-08-15 15:14:15.000000 +00:00');
INSERT INTO userlogs (usernm,state,start_time,end_time) VALUES ('user1', 'Work', '2022-08-15 15:14:15.000000 +00:00', '2022-08-15 18:07:50.000000 +00:00');
INSERT INTO userlogs (usernm,state,start_time,end_time) VALUES ('user1', 'Break', '2022-08-15 18:07:50.000000 +00:00', '2022-08-15 19:07:18.000000 +00:00');
INSERT INTO userlogs (usernm,state,start_time,end_time) VALUES ('user1', 'Work', '2022-08-15 19:07:18.000000 +00:00', '2022-08-15 19:25:31.000000 +00:00');
INSERT INTO userlogs (usernm,state,start_time,end_time) VALUES ('user1', 'Work', '2022-08-15 19:25:31.000000 +00:00', '2022-08-15 19:34:57.000000 +00:00');
INSERT INTO userlogs (usernm,state,start_time,end_time) VALUES ('user1', 'Work', '2022-08-15 19:34:57.000000 +00:00', '2022-08-15 20:10:57.000000 +00:00');
INSERT INTO userlogs (usernm,state,start_time,end_time) VALUES ('user1', 'Logged Out', '2022-08-15 20:10:57.000000 +00:00', null);

SELECT usernm, (count(*) * interval '1 min')::text AS work_interval
FROM (
SELECT usernm, generate_series (start_time, end_time - interval '1 min', interval '1 min') AS h
FROM userlogs where state='Work'
) sub
WHERE EXTRACT(ISODOW FROM h) < 6
AND h::time >= '20:00'
AND h::time <= '21:00'
GROUP BY 1
ORDER BY 1;

OUTPUT :

 
usernm work_interval
user1 00:10:00

Need Oracle SQL to split up date/time range by day

It is possible to do this in SQL. There are two tricks. The first is generating a series of numbers, which you can do with a CTE using connect.

The second is putting together the right logic to expand the dates, while keeping the right times for the beginning and end.

The following is an example:

with n as (
select level n
from dual connect by level <= 20
),
t as (
select 1 as id, to_date('01/01/2000 4', 'mm/dd/yyyy hh') as StartDate, to_date('01/03/2000 6', 'mm/dd/yyyy hh') as EndDate from dual union all
select 2 as id, to_date('01/04/2000 8', 'mm/dd/yyyy hh') as StartDate, to_date('01/04/2000 12', 'mm/dd/yyyy hh') as EndDate from dual union all
select 3 as id, to_date('01/05/2000', 'mm/dd/yyyy') as StartDate, to_date('01/06/2000', 'mm/dd/yyyy') as EndDate from dual
)
select t.id,
(case when n = 1 then StartDate
else trunc(StartDate + n - 1)
end) as StartDate,
(case when trunc(StartDate + n - 1) = trunc(enddate)
then enddate
else trunc(StartDate + n)
end)
from t join
n
on StartDate + n - 1 <= EndDate
order by id, StartDate

Here it is on SQLFiddle.

Oracle splitting date range into day and custom time intervals

You can use:

WITH SAMPLE (ID, startdate, enddate ) AS (
SELECT 1,
TO_DATE('2022-02-03 08:40', 'YYYY-MM-DD HH24:MI'),
TO_DATE('2022-02-04 10:07', 'YYYY-MM-DD HH24:MI')
FROM DUAL
UNION ALL
SELECT 2,
TO_DATE('2022-02-03 08:40', 'YYYY-MM-DD HH24:MI'),
TO_DATE('2022-02-04 02:07', 'YYYY-MM-DD HH24:MI')
FROM DUAL
UNION ALL
SELECT 3,
TO_DATE('2022-02-03 04:40', 'YYYY-MM-DD HH24:MI'),
TO_DATE('2022-02-04 02:07', 'YYYY-MM-DD HH24:MI')
FROM DUAL
)
SELECT ID,
o.type,
GREATEST(L.start_date + o.start_offset, s.startdate) AS startdate,
LEAST(L.start_date + o.end_offset, s.enddate) AS enddate
FROM SAMPLE s
CROSS JOIN LATERAL (
SELECT TRUNC(startdate - INTERVAL '6' HOUR)
+ INTERVAL '6' HOUR
+ LEVEL - 1 AS start_date
FROM DUAL
CONNECT BY
TRUNC(startdate - INTERVAL '6' HOUR)
+ INTERVAL '6' HOUR
+ LEVEL - 1
< ENDDATE
) L
CROSS JOIN (
SELECT 'DAY' AS type,
INTERVAL '0' HOUR AS start_offset,
INTERVAL '16' HOUR AS end_offset
FROM DUAL
UNION ALL
SELECT 'NIGHT' AS type,
INTERVAL '16' HOUR AS start_offset,
INTERVAL '24' HOUR AS end_offset
FROM DUAL
) o
WHERE L.start_date + o.start_offset < s.enddate
AND L.start_date + o.end_offset > s.startdate;

Which outputs:





























































IDTYPESTARTDATEENDDATE
1DAY2022-02-03 08:40:002022-02-03 22:00:00
1NIGHT2022-02-03 22:00:002022-02-04 06:00:00
1DAY2022-02-04 06:00:002022-02-04 10:07:00
2DAY2022-02-03 08:40:002022-02-03 22:00:00
2NIGHT2022-02-03 22:00:002022-02-04 02:07:00
3NIGHT2022-02-03 04:40:002022-02-03 06:00:00
3DAY2022-02-03 06:00:002022-02-03 22:00:00
3NIGHT2022-02-03 22:00:002022-02-04 02:07:00

Calculate TIME Difference in ORACLE for same field in different row with output in HH:MM:SS format

Try below. Here '1900-01-01' is dummy for TO_CHAR to work correctly.

WITH MAIN
AS (SELECT TO_DATE ('20200515 19:11:54', 'yyyymmdd hh24:mi:ss') DAT1,
TO_DATE ('20200515 18:11:54', 'yyyymmdd hh24:mi:ss') DAT2
FROM DUAL)
SELECT DAT1,
DAT2,
TO_CHAR (DATE '1900-01-01' + ABS (DAT1 - DAT2), 'HH24:MI:SS')
FROM MAIN

Same function with using LAG.

WITH MAIN
AS (SELECT TO_DATE ('20200515 19:11:54', 'yyyymmdd hh24:mi:ss') DAT1,
TO_DATE ('20200515 18:11:54', 'yyyymmdd hh24:mi:ss') DAT2
FROM DUAL
UNION ALL
SELECT TO_DATE ('20200514 19:12:54', 'yyyymmdd hh24:mi:ss') DAT1,
TO_DATE ('20200514 16:12:54', 'yyyymmdd hh24:mi:ss') DAT2
FROM DUAL
)
SELECT DAT1,
DAT2,
LAG(DAT1,1)OVER(ORDER BY DAT1) LAG,
TO_CHAR (DATE '1900-01-01' + ABS ( LAG(DAT1,1)OVER(ORDER BY DAT1) - DAT1), 'HH24:MI:SS')
FROM MAIN


Related Topics



Leave a reply



Submit