Domanda

Ho questa tabella:

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 &&
    )
);
.

Quando un utente registra il login_time salvato con tsrange(login_time,logout_time).
Ora provo a cercare un utente che accede a:

-- ('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')
.

Ho questa query ma senza risultato utile

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;
.

Qualcuno sa come posso scrivere una query che cerca un utente che effettua un accesso a 3-4 TimePoints.

È stato utile?

Soluzione

Questo è un tipico caso di divisione relazionale . Ci sono molti modi per risolverlo. Questo dovrebbe essere tra i più veloci e più semplici:

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;
.

Hai un vincolo di esclusione in posizione, ma lo stesso potrebbe essere registrato in più volte durante un singolo intervallo di test, quindi abbiamo bisogno di GROUP BY o DISTINCT.

Abbiamo assemblato un intero arsenale di tecniche in questa risposta correlata:
Come filtrare SQL Risultati A has-molti-through relation

Per evitare duplicati per iniziare e allo stesso tempo recuperare un'intera fila da una tabella users (che non è nella tua domanda, ma probabilmente esiste), questo modulo potrebbe essere più veloce:

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;
.

Il vincolo di esclusione sui login è strumentale per queste query. È implementato da un indice di GIST multicolumn che rende queste look-up molto veloci.

Altri suggerimenti

Un modo per affrontare questo tipo di query è utilizzando l'aggregazione con la clausola having per il filtraggio.È un po 'poco chiaro ciò che la tua query sta facendo veramente (cosa è timed_requests contro logins ad esempio).

I seguenti incapsulano la logica:

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;
.

Ogni condizione nella clausola having conta il numero di righe che soddisfano la particolare condizione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top