Reputation: 3923
Let's say I have a table that looks something like this:
id start end
-----+-----------------------+---------------------
1 | Jan 15 2:30 pm | Jan 15 2:45 pm
2 | Jan 15 3:16 pm | Jan 16 5:14 am
: | : | :
What I need to do is get the total sum of the duration of time that is between a certain time range.
For example, let's say I needed the sum of all time that was between query_start = Jan 15 at 1pm
and query_end = Jan 15 at 3pm
. That would be simple, because I could look for time where start >= query_start
and end <= query_end
and then sum up the differences between start
and end
.
However, it gets a little more complicated when the query range does not completely encompass the range in the row.
For example, if I was querying for all time between Jan 15 2:15 pm
and Jan 15 2:35 pm
, then the correct result would be 5 minutes, because 5 minutes of row 1
was between that time.
Also, for instance, if my query range was between Jan 15 2:40
and Jan 15 2:41
then I would want to get a sum total of 1 minute.
All I really need here is the sum, not the individual rows.
Does PostgreSQL support this natively?
If not, does anyone have any ideas about how I could write a function to do it?
Upvotes: 0
Views: 920
Reputation: 6589
You can use ranges to solve this.
First find the ranges you are interested in with the && operator like
SELECT * FROM foo WHERE tsrange(start, stop) && tsrange('(2016-01-07 10:30', '2016-01-07 16:30)')
Then use the intersect * operator to cut away the time outside the range/interval you are interested in.
SELECT tsrange('(2016-01-07 10:30', '2016-01-07 16:30)') * tsrange('(2016-01-07 13:00', '2016-01-07 13:30)')
What's left is to sum it up. A complete query would be something like
SELECT
SUM(
upper(tsrange(start, stop) * tsrange('(2016-01-07 10:30', '2016-01-07 16:30)'))
-
lower(tsrange(start, stop) * tsrange('(2016-01-07 10:30', '2016-01-07 16:30)')))
FROM foo WHERE tsrange(start, stop) && tsrange('(2016-01-07 10:30', '2016-01-07 16:30)');
Adjust your ranges to be open or closed depending on how you want the result.
Upvotes: 0
Reputation: 3314
I haven't got time to test this, but I think that you can make a query where rather than do start > query_start and end < query_end you can do
query_start < end and query_end > start
as the where clause, which will give you any periods that overlap. Then rather than summing the end - start for each period, you can remove the overlap with
sum(min(end, query_end) - max(query_start, start))
I haven't tested this.
Upvotes: 1