Afternoon All,
I'm pulling my non-existent hair out on this...
Trying to work out the average duration a specific task occurs from start to finish.
Simplified version of table:
| Code | Dstamp | ID |
|---|---|---|
| Allocate | 14-SEP-22 10.42.40.530304000 | 1234 |
| Replenish | 14-SEP-22 19.42.40.530304000 | 1234 |
| Allocate | 15-SEP-22 13.36.40.530304000 | 5678 |
| Replenish | 15-SEP-22 18.12.40.530304000 | 5678 |
| Allocate | 16-SEP-22 09.42.40.530304000 | 9101 |
| Replenish | 16-SEP-22 21.42.40.530304000 | 9101 |
I'm pretty new to SQL, so it's probably something really simple - I have a feeling that I would either need to use LAG or Union (or possibly both!) but not sure if I'm overthinking it!
There are a number of different lines in the table with the same ID but these will be ignored under a where clause, so will just be focusing on the Allocate/Replenish rows.
Any help will be appreciated!
CodePudding user response:
You could use lead/lag, but you can also use aggregation, which could be conditional to effectively ignore any that only have one of the codes:
select id,
max(case when code = 'Replenish' then dstamp end)
- min(case when code = 'Allocate' then dstamp end) as diff
from your_table
group by id
| ID | DIFF |
|---|---|
| 1234 | 000000000 09:00:00.000000000 |
| 5678 | 000000000 04:36:00.000000000 |
| 9101 | 000000000 12:00:00.000000000 |
But you can't directly average an interval. One option is to convert the intervals to the equivalent number of seconds, which you can do with extract(), maybe using a CTE to avoid repeating the calculation:
with cte (id, diff) as (
select id,
max(case when code = 'Replenish' then dstamp end)
- min(case when code = 'Allocate' then dstamp end)
from your_table
group by id
)
select id, diff,
extract(day from diff) * 86400
extract(hour from diff) * 3600
extract(minute from diff) * 60
extract(second from diff) as diff_seconds
from cte
| ID | DIFF | DIFF_SECONDS |
|---|---|---|
| 1234 | 000000000 09:00:00.000000000 | 32400 |
| 5678 | 000000000 04:36:00.000000000 | 16560 |
| 9101 | 000000000 12:00:00.000000000 | 43200 |
then average those numbers, and convert that back to an interval (or whatever data type you need):
with cte (id, diff) as (
select id,
max(case when code = 'Replenish' then dstamp end)
- min(case when code = 'Allocate' then dstamp end)
from your_table
group by id
)
select avg(
extract(day from diff) * 86400
extract(hour from diff) * 3600
extract(minute from diff) * 60
extract(second from diff)
) * interval '1' second as avg_diff
from cte
| AVG_DIFF |
|---|
| 000000000 08:32:00.000000000 |
If you have an ID that only has 'Allocate' then that won't affect the result; and this should work even if you didn't filter out the other code values first.
