This request will do everything (adapted to your added fiddle):
SELECT a.ts, g.*, round((a.ct * numeric '100') / g.capacity, 2) AS pct FROM ( SELECT ts, c.garage_id, count(*) AS ct FROM generate_series(timestamp '2015-06-01 00:00'
SQL Fiddle
If returned_at IS NULL , this request assumes that the vehicle is still in use. Therefore, NULL should not occur for other cases or you have a calculation error.
First, I create time series using the convenient generate_series() .
Then join the meetings where the timestamp falls inside the reservation.
I assume that each assignment includes a lower and exclusive upper timestamp, as this is a widespread convention.
The totality and counting before we join the garages (faster this way). For comparison:
Percentage calculations in an external SELECT .
I multiply the bigint number by numeric (or optionally real or float ) to save fractional digits that will be truncated in integer division. Then I round to two fractional digits.
Note that this is not exactly the average percentage of each 4-hour period, but only the current percentage at each point in time, which is an approximation of the true average. You can start with an odd timestamp, for example, โ2015-06-01 01:17โ, so as not to fall between orders that are likely to roll over after a full hour or something else that can increase the average approximation error.
You can make an accurate calculation for 4 periods, but it is more complicated. One simple way would be to reduce the interval to 10 minutes or some detail that would be detailed enough to capture the full picture.
Related (with example for exact calculation):