Squashing values in a column by another timestamp colum - sql

I have a following data which shows the status of a support ticket:
Edit:
More concise and generic example:
STATUS SEQ_NO
New 1
Open 2
Open 3
Open 4
Queued 5
Open 6
Open 7
Open 8
Completed 9
Completed 10
Completed 11
Closed 12
From this, I would like to extract the records,
STATUS SEQ_NO
New 1
Open 2
Queued 5
Open 6
Completed 9
Closed 12
Original question:
-- SELECT status, start_time FROM events_tab ORDER BY start_time;
STATUS START_TIME
New 30/09/2014 3:48:10 PM -- I want this record,
Open 30/09/2014 3:48:10 PM -- and this,
Open 1/10/2014 10:41:57 AM
Open 4/03/2015 9:59:04 AM
Queued 18/06/2015 1:31:30 PM -- and this,
Open 20/06/2015 10:10:47 PM -- and this,
Open 20/06/2015 11:20:11 PM
Open 27/06/2015 1:18:50 PM
Completed 27/06/2015 1:22:08 PM -- and this,
Completed 28/09/2015 9:31:55 AM
Completed 5/10/2015 11:57:38 AM
Closed 11/01/2016 9:31:26 AM -- and this.
These are events that happened in each state. I want to make a timeline of state changes from it.
I want to squash these records such that only the very first row of a group is show. However, notice that there are actually two groups of Open status. So I should get two records with Open status.
Basically I want the following result:
STATUS START_TIME
New 30/09/2014 3:48:10 PM
Open 30/09/2014 3:48:10 PM
Queued 18/06/2015 1:31:30 PM
Open 20/06/2015 10:10:47 PM
Completed 27/06/2015 1:22:08 PM
Closed 11/01/2016 9:31:26 AM
How can I achieve this with an SQL statement?
I have tried,
SELECT status, MIN(start_time)
FROM events_tab
GROUP BY status;
But this does not include multiple records in Open status, as my intention above.

You can use the Tabibitosan technique to achieve this goal:
WITH your_table AS (SELECT 'New' status, to_date('30/09/2014 03:48:10 PM', 'dd/mm/yyyy hh:mi:ss AM') start_time FROM dual UNION ALL
SELECT 'Open' status, to_date('30/09/2014 03:48:10 PM', 'dd/mm/yyyy hh:mi:ss AM') start_time FROM dual UNION ALL
SELECT 'Open' status, to_date('1/10/2014 10:41:57 AM', 'dd/mm/yyyy hh:mi:ss AM') start_time FROM dual UNION ALL
SELECT 'Open' status, to_date('4/03/2015 09:59:04 AM', 'dd/mm/yyyy hh:mi:ss AM') start_time FROM dual UNION ALL
SELECT 'Queued' status, to_date('18/06/2015 01:31:30 PM', 'dd/mm/yyyy hh:mi:ss AM') start_time FROM dual UNION ALL
SELECT 'Open' status, to_date('20/06/2015 10:10:47 PM', 'dd/mm/yyyy hh:mi:ss AM') start_time FROM dual UNION ALL
SELECT 'Open' status, to_date('20/06/2015 11:20:11 PM', 'dd/mm/yyyy hh:mi:ss AM') start_time FROM dual UNION ALL
SELECT 'Open' status, to_date('27/06/2015 01:18:50 PM', 'dd/mm/yyyy hh:mi:ss AM') start_time FROM dual UNION ALL
SELECT 'Completed' status, to_date('27/06/2015 01:22:08 PM', 'dd/mm/yyyy hh:mi:ss AM') start_time FROM dual UNION ALL
SELECT 'Completed' status, to_date('28/09/2015 09:31:55 AM', 'dd/mm/yyyy hh:mi:ss AM') start_time FROM dual UNION ALL
SELECT 'Completed' status, to_date('5/10/2015 11:57:38 AM', 'dd/mm/yyyy hh:mi:ss AM') start_time FROM dual UNION ALL
SELECT 'Closed' status, to_date('11/01/2016 09:31:26 AM', 'dd/mm/yyyy hh:mi:ss AM') start_time FROM dual)
SELECT status,
MIN(start_time) start_time
FROM (SELECT status,
start_time,
row_number() OVER (ORDER BY start_time, status) - row_number() OVER (PARTITION BY status ORDER BY start_time, status) grp
FROM your_table)
GROUP BY status, grp
ORDER BY start_time, status;
STATUS START_TIME
--------- -------------------
New 30/09/2014 15:48:10
Open 30/09/2014 15:48:10
Queued 18/06/2015 13:31:30
Open 20/06/2015 22:10:47
Completed 27/06/2015 13:22:08
Closed 11/01/2016 09:31:26
N.B. Since you have rows with different statuses having the same start_time, I have added status into the order by, in order to get the results you were after. I don't know if that was a typo, or whether multiple rows really can have the same date.
Also, I assume that the data in your example refers to one "thing", but in your real table, you can have multiple "things" each with their own set of statuses etc.
In that case, you would need to add the column(s) that differentiate the "things" (e.g. id or event_name or etc) into both row_number() analytic functions. (e.g. row_number() over (partition by <thing column(s)> order by start_time, status))

You can also try the SQL for Pattern Matching
WITH tickets(STATUS, START_TIME) AS (
SELECT 'New', TO_DATE('30/09/2014 3:48:10 PM', 'dd/mm/yyyy hh:mi:ss AM') FROM dual UNION ALL
SELECT 'Open', TO_DATE('30/09/2014 3:48:10 PM', 'dd/mm/yyyy hh:mi:ss AM') FROM dual UNION ALL
SELECT 'Open', TO_DATE('1/10/2014 10:41:57 AM', 'dd/mm/yyyy hh:mi:ss AM') FROM dual UNION ALL
SELECT 'Open', TO_DATE('4/03/2015 9:59:04 AM', 'dd/mm/yyyy hh:mi:ss AM') FROM dual UNION ALL
SELECT 'Queued', TO_DATE('18/06/2015 1:31:30 PM', 'dd/mm/yyyy hh:mi:ss AM') FROM dual UNION ALL
SELECT 'Open', TO_DATE('20/06/2015 10:10:47 PM', 'dd/mm/yyyy hh:mi:ss AM') FROM dual UNION ALL
SELECT 'Open', TO_DATE('20/06/2015 11:20:11 PM', 'dd/mm/yyyy hh:mi:ss AM') FROM dual UNION ALL
SELECT 'Open', TO_DATE('27/06/2015 1:18:50 PM', 'dd/mm/yyyy hh:mi:ss AM') FROM dual UNION ALL
SELECT 'Completed', TO_DATE('27/06/2015 1:22:08 PM', 'dd/mm/yyyy hh:mi:ss AM') FROM dual UNION ALL
SELECT 'Completed', TO_DATE('28/09/2015 9:31:55 AM', 'dd/mm/yyyy hh:mi:ss AM') FROM dual UNION ALL
SELECT 'Completed', TO_DATE('5/10/2015 11:57:38 AM', 'dd/mm/yyyy hh:mi:ss AM') FROM dual UNION ALL
SELECT 'Closed', TO_DATE('11/01/2016 9:31:26 AM', 'dd/mm/yyyy hh:mi:ss AM') FROM dual)
SELECT STATUS, START_TIME
FROM tickets
MATCH_RECOGNIZE (
ORDER BY START_TIME
MEASURES
START_TIME AS START_TIME,
STATUS as STATUS
PATTERN ( CHNG )
DEFINE
CHNG AS CHNG.STATUS <> PREV(CHNG.STATUS) OR PREV(CHNG.STATUS) IS NULL
)
STATUS START_TIME
========== ====================
New 30.09.2014 15:48:10
Open 30.09.2014 15:48:10
Queued 18.06.2015 13:31:30
Open 20.06.2015 22:10:47
Completed 27.06.2015 13:22:08
Closed 11.01.2016 09:31:26
CHNG.STATUS <> PREV(CHNG.STATUS) matches each row where STATUS is different to previous row. PREV(CHNG.STATUS) IS NULL is used to get also the very first row.

use row_number window function
select STATUS ,START_TIME from
(
select STATUS,START_TIME,
row_number() over (partition by STATUS,EXTRACT(YEAR FROM START_TIME) order by START_TIME) rn
from events_tab
) t where rn=1

Use LAG Function as you need to track the change in status:
https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=38a991b698c858f6f0417c7d4c0dc9d3
with cte1 (st,dt) as
(
select 'New' as st, '30/09/2014 3:48:10 PM' as dt from dual
union all
select 'Open' as st, '30/09/2014 3:48:10 PM' as dt from dual
union all
select 'Open' as st, '20/09/2014 3:48:10 PM' as dt from dual
union all
select 'Qued' as st, '18/06/2015 1:31:30' as dt from dual
)
select st, min(case when st<>prev_order_date then dt else dt end) as d
from
(
SELECT st, dt,
LAG (st,1) OVER (ORDER BY st) AS prev_order_date
FROM cte1
)a
group by st

Related

time range between 2 dates

I have following script
with first_step as
(
SELECT
1 as MY_TYPE,
2373 as my_id
,to_date('15.02.23 17:00' , 'dd.mm.yyyy HH24:MI') AS time_from
,to_date('17.02.23 12:00' , 'dd.mm.yyyy HH24:MI')AS time_till
from dual
union all
SELECT
1 as MY_TYPE,
2373 as my_id
,to_date('16.02.23 14:00' , 'dd.mm.yyyy HH24:MI') AS time_from
,to_date('16.02.23 15:00' , 'dd.mm.yyyy HH24:MI')AS time_till
from dual
union all
SELECT
0 as MY_TYPE,
2373 as my_id
,to_date('14.02.23 22:00' , 'dd.mm.yyyy HH24:MI') AS time_from
,to_date('16.02.23 18:00' , 'dd.mm.yyyy HH24:MI')AS time_till
from dual
),
second_step as
(
select
MY_TYPE,
my_id,
to_date(to_char(time_from +(column_value-1), 'dd.mm.yyyy'),'dd.mm.yyyy') AS my_date,
case when trunc(time_from) < to_date(to_char(time_from +(column_value-1), 'dd.mm.yyyy'),'dd.mm.yyyy') then '00:00' else to_char(time_from,'HH24:MI') end time_from,
case when trunc(time_till) > to_date(to_char(time_from +(column_value-1), 'dd.mm.yyyy'),'dd.mm.yyyy') then '23:59' else replace(to_char(time_till,'HH24:MI'),'00:00','23:59') end time_till
from first_step
CROSS JOIN TABLE ( CAST(MULTISET(
SELECT
level
from
dual
CONNECT BY time_from + level-1 <= time_till
) AS sys.odcinumberlist) ) n
)
select * from second_step
order by
my_date,time_from, time_till
that is what I get
But I need that
So, we have entries which are on the same day, but also entries lasting multiple days. The single day entries should stay as they are, but the multiple days should be stretched.Currently my multiple days entries are not represented correctly. What is wrong with my script?
You can use a recursive query:
with first_step (my_type, my_id, time_from, time_till) as (
SELECT 1,
2373,
to_date('15.02.2023 17:00', 'dd.mm.yyyy HH24:MI'),
to_date('17.02.2023 12:00', 'dd.mm.yyyy HH24:MI')
from dual
union all
SELECT 1,
2373,
to_date('16.02.2023 14:00', 'dd.mm.yyyy HH24:MI'),
to_date('16.02.2023 15:00', 'dd.mm.yyyy HH24:MI')
from dual
union all
SELECT 0,
2373,
to_date('14.02.2023 22:00', 'dd.mm.yyyy HH24:MI'),
to_date('16.02.2023 18:00', 'dd.mm.yyyy HH24:MI')
from dual
),
days (my_type, my_id, time_from, day_end, time_till) AS (
SELECT my_type,
my_id,
time_from,
TRUNC(time_from) + INTERVAL '23:59:59' HOUR TO SECOND,
time_till
FROM first_step
UNION ALL
SELECT my_type,
my_id,
day_end + INTERVAL '1' SECOND,
day_end + INTERVAL '1' DAY,
time_till
FROM days
WHERE day_end < time_till
)
SELECT my_type,
my_id,
time_from,
LEAST(day_end, time_till) AS time_till
FROM days
ORDER BY my_id, time_from, time_till;
Or a hierarchical query:
with first_step (my_type, my_id, time_from, time_till) as (
SELECT 1,
2373,
to_date('15.02.2023 17:00', 'dd.mm.yyyy HH24:MI'),
to_date('17.02.2023 12:00', 'dd.mm.yyyy HH24:MI')
from dual
union all
SELECT 1,
2373,
to_date('16.02.2023 14:00', 'dd.mm.yyyy HH24:MI'),
to_date('16.02.2023 15:00', 'dd.mm.yyyy HH24:MI')
from dual
union all
SELECT 0,
2373,
to_date('14.02.2023 22:00', 'dd.mm.yyyy HH24:MI'),
to_date('16.02.2023 18:00', 'dd.mm.yyyy HH24:MI')
from dual
)
SELECT my_type,
my_id,
GREATEST(time_from, day_start) AS time_from,
LEAST(time_till, day_end) AS time_till
FROM first_step f
CROSS JOIN LATERAL (
SELECT TRUNC(f.time_from) + LEVEL - INTERVAL '1' DAY AS day_start,
TRUNC(f.time_from) + LEVEL - INTERVAL '1' SECOND AS day_end
FROM dual
CONNECT BY TRUNC(f.time_from) + LEVEL - 1 < f.time_till
)
ORDER BY my_id, time_from, time_till
Which both output:
MY_TYPE
MY_ID
TIME_FROM
TIME_TILL
0
2373
2023-02-14 22:00:00
2023-02-14 23:59:59
0
2373
2023-02-15 00:00:00
2023-02-15 23:59:59
1
2373
2023-02-15 17:00:00
2023-02-15 23:59:59
0
2373
2023-02-16 00:00:00
2023-02-16 18:00:00
1
2373
2023-02-16 00:00:00
2023-02-16 23:59:59
1
2373
2023-02-16 14:00:00
2023-02-16 15:00:00
1
2373
2023-02-17 00:00:00
2023-02-17 12:00:00
Note: to_date('16.02.23 14:00', 'dd.mm.yyyy HH24:MI') will give you the year 0023 and not 2023. If you want 2023 then use a 4-digit year or the format model RR or YY.
fiddle

finding the time periods an issuer has not called the service from a given time period

I have a table that contains the time periods when an issuer calls the service. this table can have overlapping and non overlapping time periods:
with mht_issuer_revoked_call (issuerid, startdate, enddate) as (values
(4, to_date('25-11-2022', 'dd-mm-yyyy'), to_date('25-11-2022 12:00:00', 'dd-mm-yyyy hh24:mi:ss'),
(4, to_date('25-11-2022 12:00:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('26-11-2022', 'dd-mm-yyyy'),
(40, to_date('25-11-2022', 'dd-mm-yyyy'), to_date('25-11-2022 06:00:00', 'dd-mm-yyyy hh24:mi:ss'),
(40, to_date('25-11-2022 06:00:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('25-11-2022 12:00:00', 'dd-mm-yyyy hh24:mi:ss'),
(40, to_date('25-11-2022 11:30:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('25-11-2022 18:00:00', 'dd-mm-yyyy hh24:mi:ss'),
(40, to_date('25-11-2022 18:30:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('25-11-2022 19:30:00', 'dd-mm-yyyy hh24:mi:ss'),
(50, to_date('25-11-2022', 'dd-mm-yyyy'), to_date('25-11-2022 12:00:00', 'dd-mm-yyyy hh24:mi:ss'),
(50, to_date('25-11-2022 11:00:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('26-11-2022 01:30:00', 'dd-mm-yyyy hh24:mi:ss'),
(40, to_date('25-11-2022 19:31:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('26-11-2022', 'dd-mm-yyyy'),
(50, to_date('25-11-2022 23:10:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('25-11-2022 23:30:00', 'dd-mm-yyyy hh24:mi:ss'),
(50, to_date('25-11-2022 23:30:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('25-11-2022 23:45:00', 'dd-mm-yyyy hh24:mi:ss'),
(50, to_date('25-11-2022 23:50:00', 'dd-mm-yyyy hh24:mi:ss'), to_date('25-11-2022 23:55:00', 'dd-mm-yyyy hh24:mi:ss')
)
i managed to merge the time periods and new time periods dont have any overlapping with each other. my output is as follows:
with issuer_calls_merged (issuerid, start_date_time, end_date_time) as (values
(4 ,'11/25/2022' , '11/26/2022'),
(40 ,'11/25/2022', '11/25/2022 6:00:00 PM'),
(40 ,'11/25/2022 6:30:00 PM', '11/25/2022 7:30:00 PM'),
(40 ,'11/25/2022 7:31:00 PM', '11/26/2022' ),
(50 ,'11/25/2022', '11/26/2022 1:30:00 AM')
)
i am trying to write a procedure that gets FromDate and EndDate as input parameters and for each issuer calculates how many minutes are not covered according to retrieved FromDate and EndDate Parameters.
for example i will give these parameters:
FromDate := '11/20/2022'
EndDate := '11/28/2022'
then according to inserted time periods in issuer_calls table, for issuerid 40 i expect this output:
| issuerid | start_date_time(uncovered) | end_date_time(uncovered) | uncovered_time_minutes
| 40 | 11/20/2022 | 11/25/2022 | 7200
| 40 | 11/25/2022 6:00:00 PM | 11/25/2022 6:30:00 PM | 30
| 40 | 11/25/2022 7:30:00 PM | 11/25/2022 7:31:00 PM | 1
| 40 | 11/26/2022 | 11/28/2022 | 2880
i tried to do the job with procedure bellow:
create or replace procedure GAP(out_res out sys_refcursor,
in_FromDate mht_issuer_revoked_call.startdate%type,
in_EndDate mht_issuer_revoked_call.enddate%type
) AS
BEGIN
**-- i tried to compare the given time period(FromDate-EndDate) with previous merged time periods and calculate the gaps and then union with previous gap**
open out_res for
select ut.issuerid,
ut.startdate,
ut.enddate,
ut.initialgap as gap
from
(
with minStartDate as
(
select r.issuerid,
min(r.startdate) as min_StartDate
from mht_issuer_revoked_call r
group by r.issuerid
)
select m.issuerid,
in_FromDate as StartDate,
case
when m.min_StartDate >= in_EndDate then in_EndDate
else m.min_StartDate
end as EndDate,
case
when m.min_StartDate >= in_EndDate then (in_EndDate - in_FromDate + 1)*24*60
else (min_StartDate - in_FromDate + 1)*24*60
end as initialgap
from minStartDate m
union all
**--- bellow part merges the time periods and calculate the gaps between them**
SELECT issuerid,
end_date_time,
next_row_start,
(next_row_start - end_date_time)*24*60 as gap
from
(
SELECT issuerid,
start_date_time,
end_date_time,
case
when lead(start_date_time) over(partition by issuerid order by start_date_time) is null then end_date_time
else lead(start_date_time) over(partition by issuerid order by start_date_time)
end as next_row_start
FROM (
SELECT issuerid,
LAG( dt ) OVER ( PARTITION BY issuerid ORDER BY dt ) AS start_date_time,
dt AS end_date_time,
start_end
FROM (
SELECT issuerid,
dt,
CASE SUM( value ) OVER ( PARTITION BY issuerid ORDER BY dt ASC, value DESC, ROWNUM ) * value
WHEN 1 THEN 'start'
WHEN 0 THEN 'end'
END AS start_end
FROM mht_issuer_revoked_call
UNPIVOT ( dt FOR value IN ( startdate AS 1, enddate AS -1 ) )
)
WHERE start_end IS NOT NULL
)
WHERE start_end = 'end'
)
where (next_row_start - end_date_time) > 0
group by issuerid,next_row_start,end_date_time
) ut
order by ut.issuerid, ut.StartDate;
END gap;
but at the end i couldn't achieve the explained result above
You can get your result using just SQL and process it later. In this answer your FromDate And EndDate (P_FROM, P_UNTILL) are set to those from your question. You can define them as parameters or bind variables so you could change them. Comments are in the code.
WITH
tbl AS -- sample data
(
Select 4 "ID", To_Date('25.11.2022 00:00:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 12:00:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 4 "ID", To_Date('25.11.2022 12:00:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('26.11.2022 00:00:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 40 "ID", To_Date('25.11.2022 00:00:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 06:00:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 40 "ID", To_Date('25.11.2022 06:00:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 12:00:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 40 "ID", To_Date('25.11.2022 11:30:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 18:00:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 40 "ID", To_Date('25.11.2022 18:30:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 19:30:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 40 "ID", To_Date('25.11.2022 19:31:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('26.11.2022 00:00:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 50 "ID", To_Date('25.11.2022 00:00:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 12:00:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 50 "ID", To_Date('25.11.2022 11:00:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('26.11.2022 01:30:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 50 "ID", To_Date('25.11.2022 23:10:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 23:30:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 50 "ID", To_Date('25.11.2022 23:30:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 23:45:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual Union All
Select 50 "ID", To_Date('25.11.2022 23:50:00', 'dd.mm.yyyy hh24:mi:ss') "START_DATE", To_Date('25.11.2022 23:55:00', 'dd.mm.yyyy hh24:mi:ss') "END_DATE" From Dual
),
day_tbl AS -- create CTE to prepare your data
( Select ID,
ROW_NUMBER() OVER(Partition By ID Order By START_DATE) "RN", -- ordering ID events
START_DATE "START_DATE", To_Char(START_DATE, 'hh24:mi:ss') "START_TIME", -- just showing the time part of START_DATE
END_DATE "END_DATE", To_Char(END_DATE, 'hh24:mi:ss') "END_TIME", -- just showing the time part of END_DATE
--
To_Date('20.11.2022', 'dd.mm.yyyy') "P_FROM", -- column with P_FROM - you could define it as bind variable
To_Date('28.11.2022', 'dd.mm.yyyy') "P_UNTILL", -- column with P_FROM - you could define it as bind variable
( END_DATE - START_DATE ) * 24 * 60 "MINS" -- first calculation used for first and last row
From
(Select *
From ( -- for each ID create starting and ending row and union them with your data
Select ID "ID", START_DATE "START_DATE", END_DATE "END_DATE" From tbl Union ALL
Select ID, To_Date('20.11.2022', 'dd.mm.yyyy'), Min(START_DATE) From tbl GROUP BY ID Union All -- row with P_FROM as START_DATE - you could define it as bind variable
Select ID, Max(END_DATE), To_Date('28.11.2022', 'dd.mm.yyyy') From tbl GROUP BY ID -- row with P_UNTILL as END_DATE - you could define it as bind variable
)
Order By ID, START_DATE
)
)
SELECT
* -- you can select just the columns you need (not all of them like here)
FROM
( Select
ID, RN, START_DATE, START_TIME, END_DATE, END_TIME, P_FROM, P_UNTILL,
CASE WHEN RN = 1 Or RN = Max(RN) OVER(Partition By ID) THEN MINS -- first and last row already calculated
-- else --> second calculation for rows that are not first nor last
ELSE Round(( START_DATE - FIRST_VALUE(END_DATE) OVER(Partition By ID, TRUNC(START_DATE) Order By START_DATE Rows Between 1 Preceding And Current Row) ) * 24 * 60, 0)
END "MINS"
From
day_tbl
)
WHERE
MINS > 0 -- if you want just ID=40 here you can filter it
--
/* R e s u l t :
ID RN START_DATE START_TIME END_DATE END_TIME P_FROM P_UNTILL MINS
---------- ---------- ---------- ---------- --------- -------- --------- --------- ----------
4 1 20-NOV-22 00:00:00 25-NOV-22 00:00:00 20-NOV-22 28-NOV-22 7200
4 4 26-NOV-22 00:00:00 28-NOV-22 00:00:00 20-NOV-22 28-NOV-22 2880
40 1 20-NOV-22 00:00:00 25-NOV-22 00:00:00 20-NOV-22 28-NOV-22 7200
40 5 25-NOV-22 18:30:00 25-NOV-22 19:30:00 20-NOV-22 28-NOV-22 30
40 6 25-NOV-22 19:31:00 26-NOV-22 00:00:00 20-NOV-22 28-NOV-22 1
40 7 26-NOV-22 00:00:00 28-NOV-22 00:00:00 20-NOV-22 28-NOV-22 2880
50 1 20-NOV-22 00:00:00 25-NOV-22 00:00:00 20-NOV-22 28-NOV-22 7200
50 6 25-NOV-22 23:50:00 25-NOV-22 23:55:00 20-NOV-22 28-NOV-22 5
50 7 26-NOV-22 01:30:00 28-NOV-22 00:00:00 20-NOV-22 28-NOV-22 2790
*/
i think i could finally finish the job.
mht_issuer_revoked_call: this is the table whenever an issuer calls the service, issuerid, startdate and enddate are submitted.
MHT_ISSUER_MERGED_CALLS: the requests of issuers are merged and stored in this table.
MHT_ISSUER_UNIONED_CALLS: merged calls and input parameters are unioned and stored in this table.
create or replace procedure GAP(out_res out sys_refcursor,
in_FromDate mht_issuer_revoked_call.startdate%type,
in_EndDate mht_issuer_revoked_call.enddate%type
) as
BEGIN
delete from MHT_ISSUER_MERGED_CALLS;
commit;
insert into MHT_ISSUER_MERGED_CALLS (
select issuerid,
start_date_time as start_date,
end_date_time as end_date
FROM (
SELECT issuerid,
LAG(dt) OVER(PARTITION BY issuerid ORDER BY dt) AS start_date_time,
dt AS end_date_time,
start_end
FROM (
SELECT issuerid,
dt,
CASE SUM(value) OVER(PARTITION BY issuerid ORDER BY dt ASC, value DESC, ROWNUM) * value
WHEN 1 THEN 'start'
WHEN 0 THEN 'end'
END AS start_end
FROM mht_issuer_revoked_call UNPIVOT(dt FOR value IN(startdate AS 1, enddate AS - 1))
)
WHERE start_end IS NOT NULL
)
WHERE start_end = 'end');
commit;
delete from MHT_ISSUER_UNIONED_CALLS;
commit;
insert into MHT_ISSUER_UNIONED_CALLS
(
Select ISSUERID,
ROW_NUMBER() OVER(Partition By ISSUERID Order By START_DATE) "RN",
START_DATE "START_DATE",
To_Char(START_DATE, 'hh24:mi:ss') "START_TIME",
END_DATE "END_DATE",
To_Char(END_DATE, 'hh24:mi:ss') "END_TIME",
in_FromDate "P_FROM",
in_EndDate "P_UNTILL",
(END_DATE - START_DATE) * 24 * 60 "MINS"
From (
Select *
From (
Select issuerid "ISSUERID",
STARTDATE "START_DATE",
ENDDATE "END_DATE"
From MHT_ISSUER_MERGED_CALLS
Union ALL
Select issuerid,
case
when in_FromDate >= Min(STARTDATE) then Min(STARTDATE)
else in_FromDate
end,
case
when in_EndDate <= Min(STARTDATE) then in_EndDate
else Min(STARTDATE)
end
From MHT_ISSUER_MERGED_CALLS
GROUP BY issuerid
Union All
Select issuerid,
case
when Max(ENDDATE) <= in_FromDate then in_FromDate
else Max(ENDDATE)
end,
case
when in_EndDate <= Max(ENDDATE) then Max(ENDDATE)
else in_EndDate
end
From MHT_ISSUER_MERGED_CALLS
GROUP BY issuerid)
Order By ISSUERID, START_DATE ASC, END_DATE ASC
)
);
COMMIT;
open out_res for
select ISSUERID,
START_DATE AS START_DATE,
SELECTED_END_DATE AS END_DATE,
MINS AS GAP_MINUTES
from
(
Select ISSUERID,
RN,
START_DATE,
trunc(start_date),
START_TIME,
END_DATE,
END_TIME,
P_FROM,
P_UNTILL,
FIRST_VALUE(END_DATE) OVER(Partition By ISSUERID, TRUNC(START_DATE) Order By START_DATE Rows Between 1 Preceding And Current Row) as Selected_End_Date,
CASE
WHEN RN = 1 Or RN = Max(RN) OVER(Partition By ISSUERID) THEN MINS
ELSE
Round((START_DATE - FIRST_VALUE(END_DATE)OVER(Partition By ISSUERID, TRUNC(START_DATE) Order By START_DATE
Rows Between 1 Preceding And Current Row)) * 24 * 60,0)
END "MINS"
From MHT_ISSUER_UNIONED_CALLS
)
where mins > 0
and START_DATE >= in_FromDate and END_DATE <= in_EndDate;
END gap;

I have a table containing in_time and out_time in the format 'HH12:MI'(varchar2).I want to calculate the total time in hours between this column

select 24 * ROUND(
to_date(OUT_TIME, 'YYYY-MM-DD hh24:mi')
- to_date(IN_TIME, 'YYYY-MM-DD hh24:mi'),
2
) diff_hours
from attendances
where employee_id = 1001;
select 24 * ROUND(
to_date('2009-07-07 23:44', 'YYYY-MM-DD hh24:mi')
- to_date('2009-07-07 19:30', 'YYYY-MM-DD hh24:mi'),
2
) diff_hours
from dual;
If you stored (date)time values as strings (which is a bad idea), you'll have to use that format with to_date. Using sample data you posted:
SQL> with test (employee, in_time, out_time) as
2 (select 'Palash', '08:45 PM', '08:45 PM' from dual union all
3 select 'Palash', '07:40 PM', '07:45 PM' from dual union all
4 select 'Sagor' , '08:10 PM', '08:43 PM' from dual
5 )
6 select employee, in_time, out_time,
7 round((to_date(out_time, 'hh:mi pm') - to_date(in_time, 'hh:mi pm')) * 24, 2) diff
8 from test;
EMPLOY IN_TIME OUT_TIME DIFF
------ -------- -------- ----------
Palash 08:45 PM 08:45 PM 0
Palash 07:40 PM 07:45 PM .08
Sagor 08:10 PM 08:43 PM .55
SQL>

oracle sum limited value, extra to other variable

Query:
select sum((out_time+0) - (in_time+0))*24 man_hours
from emp a,time_sheet b
where a.SUPERVISOR='43561'
and a.EMP_ID=b.EMP_ID;
Sample data in table
emp_id in_time out_time
40716 08-07-2016 09:00 08-07-2016 18:00
40716 07-07-2016 09:00 07-07-2016 18:00
40716 06-07-2016 09:00 06-07-2016 18:00
60383 06-07-2016 09:00 06-07-2016 18:00
60383 07-07-2016 09:00 07-07-2016 18:00
41223 07-07-2016 09:00 07-07-2016 18:00
41223 08-07-2016 09:00 08-07-2016 18:00
Result: Sum of differences from above query is 45
difference between time in each row is 9 hours.
Requirement : I want only <=8 hours to sum up. >8 hours should be as other value.
Current 9*5= 45, required 8*5 = 40 and extra 5
I tried with decode, I am getting some weird results, actually I am not getting any idea in mind. Pointing out in right way would be helpful.
Thanks
This should get you started:
WITH
Timesheet_raw (emp_id, in_time, out_time) AS (
SELECT 40716, '08-07-2016 09:00', '08-07-2016 18:00' FROM DUAL UNION ALL
SELECT 40716, '07-07-2016 09:00', '07-07-2016 18:00' FROM DUAL UNION ALL
SELECT 40716, '06-07-2016 09:00', '06-07-2016 18:00' FROM DUAL UNION ALL
SELECT 60383, '06-07-2016 09:00', '06-07-2016 18:00' FROM DUAL UNION ALL
SELECT 60383, '07-07-2016 09:00', '07-07-2016 18:00' FROM DUAL UNION ALL
SELECT 41223, '07-07-2016 09:00', '07-07-2016 18:00' FROM DUAL UNION ALL
SELECT 41223, '08-07-2016 09:00', '08-07-2016 18:00' FROM DUAL
),
Timesheet (emp_id, in_time, out_time, length_of_shift) AS (
SELECT
emp_id
, TO_DATE(in_time, 'DD-MM-YYYY HH24:MI')
, TO_DATE(out_time, 'DD-MM-YYYY HH24:MI')
, (TO_DATE(out_time, 'DD-MM-YYYY HH24:MI') - TO_DATE(in_time, 'DD-MM-YYYY HH24:MI')) * 24
FROM Timesheet_raw
)
SELECT
emp_id, LEAST(length_of_shift, 8) regular, GREATEST(length_of_shift - 8, 0) overtime FROM Timesheet
;
Please comment, if and as this requires adjustment / further detail.
This is your basic query.
There is one row for every employee for every day, time spent regular and time spent overtime (if there was any).
WITH MY_TABLE AS -- Dummy data, leave this out on your enviroment
(
SELECT 40716 AS ID, TO_DATE('08-07-2016 09:00', 'DD-MM-YYYY HH24:MI') AS TIME_START, TO_DATE('08-07-2016 18:00', 'DD-MM-YYYY HH24:MI') AS TIME_END FROM DUAL UNION
SELECT 40716, TO_DATE('07-07-2016 09:00', 'DD-MM-YYYY HH24:MI'), TO_DATE('07-07-2016 18:00', 'DD-MM-YYYY HH24:MI') FROM DUAL UNION
SELECT 40716, TO_DATE('06-07-2016 09:00', 'DD-MM-YYYY HH24:MI'), TO_DATE('06-07-2016 18:00', 'DD-MM-YYYY HH24:MI') FROM DUAL UNION
SELECT 60383, TO_DATE('06-07-2016 09:00', 'DD-MM-YYYY HH24:MI'), TO_DATE('06-07-2016 18:00', 'DD-MM-YYYY HH24:MI') FROM DUAL UNION
SELECT 60383, TO_DATE('07-07-2016 09:00', 'DD-MM-YYYY HH24:MI'), TO_DATE('07-07-2016 18:00', 'DD-MM-YYYY HH24:MI') FROM DUAL UNION
SELECT 41223, TO_DATE('07-07-2016 09:00', 'DD-MM-YYYY HH24:MI'), TO_DATE('07-07-2016 18:00', 'DD-MM-YYYY HH24:MI') FROM DUAL UNION
SELECT 41223, TO_DATE('08-07-2016 09:00', 'DD-MM-YYYY HH24:MI'), TO_DATE('08-07-2016 18:00', 'DD-MM-YYYY HH24:MI') FROM DUAL)
SELECT -- Actual query
ID,
LEAST (8, TOTAL_TIME) AS REGULAR_TIME, -- MIN of actual time and 8 hours
CASE -- If he worked less than 8 hours,
-- OT is 0, otherwise actual-8
WHEN TOTAL_TIME > 8
THEN TOTAL_TIME - 8
ELSE 0
END AS OVER_TIME
FROM
(
SELECT
ID,
(TIME_END - TIME_START)*24 AS TOTAL_TIME -- Oracle date returns days,
-- multiply by 24 to have hourse
FROM
MY_TABLE
);
Result looks following
ID REGULAR_TIME OVER_TIME
40716 8 1
40716 8 1
40716 8 1
41223 8 1
41223 8 1
60383 8 1
60383 8 1
You can nest this query under another one and perform whatever you like, for example, group by id so you get total regular and over time in given time frame per empoyee.
Or sum it up, without any grouping to fulfil your original requirement.
SELECT
SUM(REGULAR_TIME),
SUM(OVER_TIME)
FROM(
-- nest previous select
);

Converting Oracle Date and Time

How do I convert the following timestamp in Oracle :
12-MAY-2013 12:00:00 AM
to this
11-MAY-2013 24:00:00
Thanks.
UPDATED A possible solution
SELECT CASE WHEN dt - TRUNC(dt) = 0
THEN TO_CHAR(TRUNC(dt) - 1, 'DD-Mon-YYYY') || ' 24:00:00'
ELSE TO_CHAR(dt, 'DD-Mon-YYYY HH24:MI:SS')
END dt
FROM
(
SELECT TO_DATE('12-MAY-2013 12:00:00 AM', 'DD-Mon-YYYY HH:MI:SS AM') dt
FROM dual
)
Output:
| DT |
------------------------
| 11-May-2013 24:00:00 |
Here is SQLFiddle demo
Original answer Try
SELECT TO_CHAR(TO_DATE('12-MAY-2013 12:00:00 AM', 'DD-Mon-YYYY HH:MI:SS AM'), 'DD-Mon-YYYY HH24:MI:SS') dt
FROM dual
Here is SQLFiddle demo
you cannot achieve this using standard Oracle functions. Oracle uses values 0 - 23, if you need to display midnight like 24:00, you have to write your own function.