I have a table which keeps track of status change of ticket.
TicketID | attribute | old_value | new_value | changeddate |
---|---|---|---|---|
101 | status | NULL | OPEN | 02-01-2025 |
101 | status | OPEN | IN Progress | 03-01-2025 |
101 | status | IN Progress | OPEN | 04-01-2025 |
101 | status | OPEN | IN Progress | 05-01-2025 |
101 | status | IN Progress | Hold | 06-01-2025 |
102 | status | NULL | IN Progress | 01-01-2025 |
102 | status | IN Progress | Hold | 03-01-2025 |
102 | status | Hold | IN Progress | 04-01-2025 |
102 | status | IN Progress | closed | 09-01-2025 |
I would like to get the duration of each status for each ticket. I have tried something like below.
select t1.ticketID,t1.new_value,TRUNC( t2.changeddate ) - TRUNC( t1.changeddate )
duration
from ticket_table t1
join ticket_table t2 on t1.ticketid = t2.ticketid
and t2.changeddate < t1.changeddate
group by t1.ticketid,t1.t1.new_value
order by t1.ticketid,t1.new_value
Even if you don't tell it in the question, I think what you noticed is that joining on t2.changeddate < t1.changeddate
will compare each status with all its predecessors.
To restrict the side-by-side comparison of statuses to only 1 row, you'd want to look for the lead()
window function that will give each row access to its immediate successor's columns:
select t.*, lead(changeddate) over (partition by ticketID order by changeddate) - t.changeddate duration
from ticket_table t
order by t.ticketid,t.changeddate;
TICKETID | ATTRIBUTE | OLD_VALUE | NEW_VALUE | CHANGEDDAT | DURATION |
---|---|---|---|---|---|
101 | status | OPEN | 02-01-2025 | 1 | |
101 | status | OPEN | IN Progress | 03-01-2025 | 1 |
101 | status | IN Progress | OPEN | 04-01-2025 | 1 |
101 | status | OPEN | IN Progress | 05-01-2025 | 1 |
101 | status | IN Progress | Hold | 06-01-2025 | |
102 | status | IN Progress | 01-01-2025 | 2 | |
102 | status | IN Progress | Hold | 03-01-2025 | 1 |
102 | status | Hold | IN Progress | 04-01-2025 | 5 |
102 | status | IN Progress | closed | 09-01-2025 |
(as output in the demo fiddle)