I have a history table which looks like this:
| ID | DateChanged | Status | |----|-------------|--------| | 1 | 01/01/2019 | Closed | | 1 | 05/01/2019 | Open | | 1 | 10/01/2019 | Open | | 2 | 01/02/2019 | Open | | 2 | 05/02/2019 | Closed | | 3 | 01/03/2019 | Open | | 3 | 01/05/2019 | Open | | 3 | 01/06/2019 | Open |
The history table is only ever updated when a change occurs, so I can say that the row returned by MAX(DateChanged)
is the current state.
Let’s say that ID
relates to a shop.
I want to be able to identify the length of time that a currently open shop has been open for.
So I would get an output like this:
| ID | DateOpen | |----|------------| | 1 | 05/01/2019 | | 2 | (null) | | 3 | 01/03/2019 |
ID = 2
would return null because it is currently closed.
I have tried using LAG
to get the previous record:
lag(status) over (partition by id order by datechanged)
But I can’t work out how to recursively increase the step until it finds “closed” rather than “open” and return that date.
How could I achieve this?
Advertisement
Answer
If there are only two statuses, you can use conditional aggregation to get the duration.
select id, (max(datechanged) - coalesce(max(case when status = 'closed' then datechanged end), min(datechanged) -- in case there are no closed records ) ) from t group by id;
To get the earliest open date, you can use window functions:
select id, min(case when status = 'Open' and (datechanged > closed_datechanged or closed_datechanged is null) then datechanged end) as open_datetime from (select t.*, max(case when status = 'Closed' then datechanged end) over (PARTITION BY id) as closed_datechanged FROM t) group by id;
The subquery gets the latest closed date time. This is then used to get the first open date after that.