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.