See this demo: http://sqlfiddle.com/#!17/2477f/1
SELECT x.timeframe, count(employee_id)
FROM (
select time '8:00' + x * interval '30 minute' as timeframe,
time '8:00' + (x+1) * interval '30 minute' as timeframe_end
from generate_series(0,10) x
) x
LEFT JOIN employee t
/* (StartA <= EndB) and (EndA >= StartB) */
ON x.timeframe <= t.timeout
AND x.timeframe_end >= t.timein
GROUP BY x.timeframe
ORDER BY 1
SELECT x.timeframe, count(employee_id)
FROM (
select time '8:00' + x * interval '30 minute' as timeframe,
time '8:00' + (x+1) * interval '30 minute' as timeframe_end
from generate_series(0,12) x
) x
LEFT JOIN employee t
/* (StartA < EndB) and (EndA > StartB) */
ON x.timeframe < t.timeout
AND x.timeframe_end > t.timein
GROUP BY x.timeframe
ORDER BY 1
| timeframe | count |
|-----------|-------|
| 08:00:00 | 0 |
| 08:30:00 | 0 |
| 09:00:00 | 1 |
| 09:30:00 | 1 |
| 10:00:00 | 2 |
| 10:30:00 | 2 |
| 11:00:00 | 3 |
| 11:30:00 | 3 |
| 12:00:00 | 3 |
| 12:30:00 | 1 |
| 13:00:00 | 1 |
| 13:30:00 | 1 |
| 14:00:00 | 0 |
The join condition uses a formula from this answer for checking whether two ranges overlap or not:
(StartA < EndB) and (EndA > StartB)
The demo also shows how the query behaves for edge cases:
(113, '13:00', '13:01'),
(115, '13:30', '14:00')
The latter employe started at 13:30 and finished at 14:00, so it is included in 13:30
timeframe, but is not included in 14:00
timeframe.
| 13:00:00 | 1 |
| 13:30:00 | 1 |
| 14:00:00 | 0 |
The problem might be with employes that start and finish a work multiple times within the same timeframe (workers who make frequent coffee breaks), for example:
(113, '13:00', '13:01'),
(113, '13:12', '13:15'),
(113, '13:22', '13:26')
for such cases you need to count distinct employees, using: count(DISTINCT employee_id)