This is a Gaps and Islands problem (article is for SQL Server but applies equally to postgresql).
The following should solve your problem
SELECT Location,
MAX(Value) AS Max,
SUM(CASE WHEN id = 'B' THEN Value END) AS Sum,
1.0 * SUM(CASE WHEN id = 'B' THEN Value END) / MAX(Value) AS SuccesRate
FROM ( SELECT *,
ROW_NUMBER() OVER(PARTITION BY Location, CASE WHEN Value = 0 THEN 1 ELSE 0 END ORDER BY metric_date) -
ROW_NUMBER() OVER(PARTITION BY Location ORDER BY metric_date) AS GroupingSet
FROM T
) AS t
WHERE Value <> 0
GROUP BY Location, GroupingSet;
The key is generating a field to group by to identify the islands, which can be done by allocating two row_numbers to each row:
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Location, CASE WHEN Value = 0 THEN 1 ELSE 0 END ORDER BY metric_date) AS RowNumInSubset,
ROW_NUMBER() OVER(PARTITION BY Location ORDER BY metric_date) AS RowNumInSet
FROM #T
ORDER BY metric_date
This produces the following:
metric_date location id value RowNumInSubset RowNumInSet
----------------------------------------------------------------------------
2020-02-07 13:00 ATL A 34 1 1
2020-02-07 13:05 ATL B 12 2 2
2020-02-07 13:10 ATL B 2 3 3
2020-02-07 13:15 ATL A 15 4 4
2020-02-07 13:20 ATL A 0 1 5
2020-02-07 13:25 ATL A 0 2 6
2020-02-07 13:30 ATL A 12 5 7
2020-02-07 13:35 ATL B 12 6 8
2020-02-07 13:40 ATL A 23 7 9
2020-02-07 13:45 ATL B 3 8 10
2020-02-07 13:50 ATL A 0 3 11
2020-02-07 13:55 ATL A 0 4 12
Then, by deducting the RowNumInSet
from the RowNumInSubset
, you will produce a constant for your islands
:
metric_date location id value RowNumInSubset RowNumInSet GroupingSet
------------------------------------------------------------------------------------
2020-02-07 13:00 ATL A 34 1 1 0
2020-02-07 13:05 ATL B 12 2 2 0
2020-02-07 13:10 ATL B 2 3 3 0
2020-02-07 13:15 ATL A 15 4 4 0
------------------------------------------------------------------------------------
2020-02-07 13:20 ATL A 0 1 5 -4
2020-02-07 13:25 ATL A 0 2 6 -4
------------------------------------------------------------------------------------
2020-02-07 13:30 ATL A 12 5 7 -2
2020-02-07 13:35 ATL B 12 6 8 -2
2020-02-07 13:40 ATL A 23 7 9 -2
2020-02-07 13:45 ATL B 3 8 10 -2
------------------------------------------------------------------------------------
2020-02-07 13:50 ATL A 0 3 11 -8
2020-02-07 13:55 ATL A 0 4 12 -8
Then finally, you can remove the rows where value = 0
, as these are just break points:
metric_date location id value RowNumInSubset RowNumInSet GroupingSet
------------------------------------------------------------------------------------
2020-02-07 13:00 ATL A 34 1 1 0
2020-02-07 13:05 ATL B 12 2 2 0
2020-02-07 13:10 ATL B 2 3 3 0
2020-02-07 13:15 ATL A 15 4 4 0
------------------------------------------------------------------------------------
2020-02-07 13:30 ATL A 12 5 7 -2
2020-02-07 13:35 ATL B 12 6 8 -2
2020-02-07 13:40 ATL A 23 7 9 -2
2020-02-07 13:45 ATL B 3 8 10 -2
Then you can perform your aggregate on each group.
Example on DB<>Fiddle