문제

나는 이 테이블을 가지고 있습니다 :

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 절은 특정 조건을 충족하는 행 수를 계산합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top