2014-01-09 95 views
0

我有这个表:选择多个时间行范围

CREATE TABLE logins(
    id SERIAL NOT NULL PRIMARY KEY, 
    login_time TSRANGE NOT NULL, 
    user_id INTEGER NOT NULL REFERENCES users(id), 
    CONSTRAINT overlapping_timeslots EXCLUDE USING GIST (
     user_id WITH =, 
     timeslot WITH && 
    ) 
); 

当login_time用户登录保存tsrange(login_time,logout_time)
现在我尝试寻找谁在登录的用户:

-- ('2013-12-31 16:40:05','2013-12-31 17:40:05') 
-- ('2014-01-04 14:27:45','2014-01-04 17:30:56') 
-- ('2014-01-05 14:59:55','2014-01-05 16:03:39') 
-- ('2014-01-01 17:20:54','2014-01-01 22:50:57') 
-- Not logged in at ('2013-12-31 18:40:05','2014-01-01 01:20:05') 

我有这个疑问,但没有有用的结果

SELECT user_id FROM (


select * from logins 
    where user_id in(select user_id from timed_requests where timeslot && tsrange('2013-12-31 16:20:05','2013-12-31 17:40:05')) 
    and user_id in(select user_id from timed_requests where timeslot && tsrange('2014-01-04 14:30:45','2014-01-04 17:20:56')) 
    and user_id in(select user_id from timed_requests where timeslot && tsrange('2014-01-05 15:09:55','2014-01-05 16:00:39')) 
    and user_id in(select user_id from timed_requests where timeslot && tsrange('2014-01-01 17:20:54','2014-01-01 22:50:57') 
    and user_id not in(select user_id from timed_requests where timeslot && tsrange('2013-12-31 18:40:05','2014-01-01 01:20:05')) 
    ) ss 
GROUP BY user_id 
order by user_id; 

有谁知道我可以编写一个查询谁的搜索在3-4个给定时间点登录的用户。

+0

表'timed_requests'进来了吗?你只引入了一个表'logins'。另外,你的Postgres版本? –

回答

1

这是一个典型的案例relational division。有很多方式来解决它。这应该是最快,最简单之中:

SELECT DISTINCT user_id 
FROM logins l1 
JOIN logins l2 USING (user_id) 
JOIN logins l3 USING (user_id) 
JOIN logins l4 USING (user_id) 
LEFT JOIN logins l5 ON t5.user_id = t1.user_id AND 
    NOT (l4.timeslot && tsrange('2013-12-31 18:40:05','2014-01-01 01:20:05')) 
WHERE l1.timeslot && tsrange('2013-12-31 16:20:05','2013-12-31 17:40:05') 
AND l2.timeslot && tsrange('2014-01-04 14:30:45','2014-01-04 17:20:56') 
AND l3.timeslot && tsrange('2014-01-05 15:09:55','2014-01-05 16:00:39') 
AND l4.timeslot && tsrange('2014-01-01 17:20:54','2014-01-01 22:50:57') 
AND l5.user_id IS NULL 
ORDER BY 1; 

你有到位的排他条件,但同样可以在多次单个测试范围内被记录下来,所以我们需要GROUP BYDISTINCT

我们已经组装的技术整体阿森纳在这个相关的答案:
How to filter SQL results in a has-many-through relation

为了避免重复,才能开始,并在同一时间从users表中检索一整行(这不是你的问题,但可能存在),此表单可能会更快:

SELECT * 
FROM users u 
WHERE EXISTS (SELECT 1 FROM logins WHERE user_id = u.user_id 
     AND timeslot && tsrange('2013-12-31 16:20:05','2013-12-31 17:40:05')) 
AND EXISTS (SELECT 1 FROM logins WHERE user_id = u.user_id 
     AND timeslot && tsrange('2014-01-04 14:30:45','2014-01-04 17:20:56')) 
AND EXISTS (SELECT 1 FROM logins WHERE user_id = u.user_id 
     AND timeslot && tsrange('2014-01-05 15:09:55','2014-01-05 16:00:39')) 
AND EXISTS (SELECT 1 FROM logins WHERE user_id = u.user_id 
     AND timeslot && tsrange('2014-01-01 17:20:54','2014-01-01 22:50:57')) 
AND NOT EXISTS (SELECT 1 FROM logins WHERE user_id = u.user_id 
     AND timeslot && tsrange('2013-12-31 18:40:05','2014-01-01 01:20:05')) 
ORDER BY u.user_id; 

登录的排除约束对这些查询有帮助。它由一个多列GiST索引实现,使这些查找速度非常快。

+0

谢谢,这作品greate – ThreeFingerMark

0

处理此类查询的一种方法是使用与having子句进行聚合进行过滤。您的查询确实在做什么有点不清楚(比如timed_requestslogins)。

下面封装的逻辑:

select user_id 
from timed_requests 
group by user_id 
having sum(case when timeslot && tsrange('2013-12-31 16:20:05','2013-12-31 17:40:05') then 1 else 0 end) > 0 and 
     sum(case when timeslot && tsrange('2014-01-04 14:30:45','2014-01-04 17:20:56') then 1 else 0 end) > 0 and 
     sum(case when timeslot && tsrange('2014-01-05 15:09:55','2014-01-05 16:00:39') then 1 else 0 end) > 0 and 
     sum(case when timeslot && tsrange('2014-01-01 17:20:54','2014-01-01 22:50:57') then 1 else 0 end) > 0 and 
     sum(case when timeslot && tsrange('2013-12-31 18:40:05','2014-01-01 01:20:05') then 1 else 0 end) > 0; 

having子句中每个条件计数满足特定条件的行的数目。