여러 시간 범위가 있는 행을 선택하세요.
-
21-12-2019 - |
문제
나는 이 테이블을 가지고 있습니다 :
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번 로그인한 사용자를 검색하는 쿼리를 작성하는 방법을 아는 사람이 있습니까?
해결책
이는 전형적인 사례이다. 관계 분할.있다 여러 가지 방법으로 그것을 해결하기 위해.이는 가장 빠르고 간단한 것 중 하나입니다.
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 BY
또는 DISTINCT
.
우리는 이와 관련된 답변에 대한 전체 기술을 모아 놓았습니다.
has-many-through 관계에서 SQL 결과를 필터링하는 방법
중복을 방지하고 동시에 전체 행을 검색하려면 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 인덱스로 구현됩니다.
다른 팁
이러한 유형의 쿼리에 접근하는 한 가지 방법은 다음과 함께 집계를 사용하는 것입니다. having
필터링 조항.귀하의 쿼리가 실제로 무엇을 하고 있는지는 약간 불분명합니다. timed_requests
~ 대 logins
예를 들어).
다음은 논리를 캡슐화합니다.
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
절은 특정 조건을 충족하는 행 수를 계산합니다.