PostgreSQL SELECT Must Match Across Multiple Rows
postgresql, postgresql-9.1, select, sql
Solution
SQL Fiddle
This exploits the boolean cast to integer as 0 or 1.
select a.*
from
availability a
inner join
(
select
user_id,
sum (
('2013-03-18 09:00:00' between starts_at and ends_at
and
'2013-03-18 16:00:00' between starts_at and ends_at
)::integer
+
('2013-03-19 08:00:00' between starts_at and ends_at
and
'2013-03-19 15:45:00' between starts_at and ends_at
)::integer
) period
from availability
group by user_id
) s on a.user_id = s.user_id
where period >= 2
Change the `where` condition to the number of periods to match.
Problem
I have a table that contains a pair of timestamps that represent a timespan. These rows are scoped by user id, and each user can have one or many rows associated with them. This data is generated from an abstract "availability" form that represents when the user is available during the week. I need to input a series of time ranges as a query and return all user ids for whom all rows in the table match. Given this table: ``` CREATE TABLE "public"."availability" ( "id" int4 NOT NULL, "user_id" int4, "starts_at" timestamp(6), "ends_at" timestamp(6), PRIMARY KEY ("id") ) WITH (OIDS=FALSE) ``` and this data: ``` User #1 is available Mon-Tue between 08:00 and 17:00 +----+---------+---------------------+---------------------+ | id | user_id | starts_at | ends_at | +----+---------+---------------------+---------------------+ | 1 | 1 | 2013-03-18 08:00:00 | 2013-03-18 17:00:00 | +----+---------+---------------------+---------------------+ | 2 | 1 | 2013-03-19 08:00:00 | 2013-03-19 17:00:00 | +----+---------+---------------------+---------------------+ User #2 is available Sun-Sat all day +----+---------+---------------------+---------------------+ | 3 | 2 | 2013-03-17 00:00:00 | 2013-03-23 23:59:59 | +----+---------+---------------------+---------------------+ User #3 is available Wed between 06:00 and 18:00 +----+---------+---------------------+---------------------+ | 4 | 3 | 2013-03-20 06:00:00 | 2013-03-20 18:00:00 | +----+---------+---------------------+---------------------+ ``` I can easily select users who are available for any of the given timestamps: ``` SELECT * FROM "public"."availability" WHERE ('2013-03-19 08:35:00' BETWEEN starts_at AND ends_at AND '2013-03-19 18:25:00' BETWEEN starts_at AND ends_at) OR ('2013-03-20 12:00:00' BETWEEN starts_at AND ends_at AND '2013-03-20 18:00:00' BETWEEN starts_at AND ends_at); +----+---------+---------------------+---------------------+ | id | user_id | starts_at | ends_at | +----+---------+---------------------+---------------------+ | 3 | 2 | 2013-03-17 00:00:00 | 2013-03-23 23:59:59 | +----+---------+---------------------+---------------------+ | 4 | 3 | 2013-03-20 06:00:00 | 2013-03-20 18:00:00 | +----+---------+---------------------+---------------------+ ``` But what I really need is to be able to query multiple timespans and return only the `user_id`s that match all of the conditions. Query: `2013-03-17 10:00:00`-`2013-03-17 16:00:00`, `2013-03-23 10:00:00`-`2013-03-23 16:00:00` should return: ``` +----+---------+---------------------+---------------------+ | id | user_id | starts_at | ends_at | +----+---------+---------------------+---------------------+ | 3 | 2 | 2013-03-17 00:00:00 | 2013-03-23 23:59:59 | +----+---------+---------------------+---------------------+ ``` Query: `2013-03-18 09:00:00`-`2013-03-18 16:00:00`, `2013-03-19 08:00:00`-`2013-03-19 15:45:00` should return: ``` +----+---------+---------------------+---------------------+ | id | user_id | starts_at | ends_at | +----+---------+---------------------+---------------------+ | 1 | 1 | 2013-03-18 08:00:00 | 2013-03-18 17:00:00 | +----+---------+---------------------+---------------------+ | 2 | 1 | 2013-03-19 08:00:00 | 2013-03-19 17:00:00 | +----+---------+---------------------+---------------------+ | 3 | 2 | 2013-03-17 00:00:00 | 2013-03-23 23:59:59 | +----+---------+---------------------+---------------------+ ``` Query: `2013-03-18 07:00:00`-`2013-03-18 18:00:00` should return nothing. SQLFiddle example