Reputation: 2305
I have a plpgsql function that generates TSTZRANGEs between two dates according to stored rules in a table called timeslots
. It is working fine but now I got a new requirement and added a TSTZRANGE field called datespan
to the timeslots
table that I also need to filter against, so that only ranges contained within this field's value are returned.
Showing you here part of the function I need to change. eid
, fromdate
and todate
are parameters passed in to the function.
SELECT TSTZRANGE(
(series::DATE + ts1.start)::TIMESTAMPTZ AT TIME ZONE 'UTC',
(series::DATE + ts1.end)::TIMESTAMPTZ AT TIME ZONE 'UTC'
) AS range
FROM
timeslots AS ts1,
generate_series(fromdate, todate, '1 day') AS series
WHERE
eid = ts1.event_id AND
EXTRACT(DOW FROM series) = ANY(ts1.weekdays)
ORDER BY
range
So what I want to do is add a filter like WHERE range <@ ts1.datespan
but I can't manage to put it anywhere that works.
EXAMPLE:
Given this information in timeslots
table:
id | start | end | weekdays | event_id | datespan
----+----------+----------+---------------+----------------------------------------------------------------
1 | 12:00:00 | 14:00:00 | {1,2,3,5,6,0} | 1 | ["2017-01-01 00:00:00+00","2017-02-01 00:00:00+00")
2 | 09:00:00 | 11:00:00 | {1,2,3,5,6,0} | 1 | [null, null)
3 | 15:00:00 | 17:00:00 | {1,2,3,5,6,0} | 1 | ["2017-02-01 00:00:00+00","2017-03-01 00:00:00+00")
The result of calling the SELECT above if eid=1
, fromdate='2017-02-02'
and todate='2017-02-03'
should look like this:
range
-----------------------------------------------------
["2017-02-02 09:00:00+00","2017-02-02 11:00:00+00")
["2017-02-02 15:00:00+00","2017-02-02 17:00:00+00")
["2017-02-03 09:00:00+00","2017-02-03 11:00:00+00")
["2017-02-03 15:00:00+00","2017-02-03 17:00:00+00")
Upvotes: 1
Views: 116
Reputation: 125254
The easiest is to use lateral
:
The LATERAL key word can precede a sub-SELECT FROM item. This allows the sub-SELECT to refer to columns of FROM items that appear before it in the FROM list. (Without LATERAL, each sub-SELECT is evaluated independently and so cannot cross-reference any other FROM item.)
select ts1.*, range
from
timeslots as ts1,
generate_series('2017-02-02'::date, '2017-02-03', '1 day') as series
cross join lateral
(
select tstzrange (
(series::date + ts1.start)::timestamptz at time zone 'utc',
(series::date + ts1.end)::timestamptz at time zone 'utc'
) as range
) range
where
1 = ts1.event_id and
extract(dow from series) = any(ts1.weekdays) and
range <@ ts1.datespan
order by range
;
id | start | end | weekdays | event_id | datespan | range
----+----------+----------+---------------+----------+-----------------------------------------------------+-----------------------------------------------------
2 | 09:00:00 | 11:00:00 | {1,2,3,5,6,0} | 1 | (,) | ["2017-02-03 09:00:00+00","2017-02-03 11:00:00+00")
3 | 15:00:00 | 17:00:00 | {1,2,3,5,6,0} | 1 | ["2017-02-01 00:00:00+00","2017-03-01 00:00:00+00") | ["2017-02-03 15:00:00+00","2017-02-03 17:00:00+00")
The alternative would be to wrap it in an outer query. Now you need to decide/inform what to do with the null boundaries.
Upvotes: 1