Ich denke, das wird den Zweck erfüllen:
WITH EVENTS AS (SELECT 'abc' usr, to_date('2016-01-01 08:00:00', 'yyyy-mm-dd hh24:mi:ss') event_ts, 'login' event_type FROM dual UNION ALL
SELECT 'abc' usr, to_date('2016-01-01 08:25:00', 'yyyy-mm-dd hh24:mi:ss') event_ts, 'Stuff' event_type FROM dual UNION ALL
SELECT 'abc' usr, to_date('2016-01-01 10:00:00', 'yyyy-mm-dd hh24:mi:ss') event_ts, 'Stuff' event_type FROM dual UNION ALL
SELECT 'abc' usr, to_date('2016-01-01 14:00:00', 'yyyy-mm-dd hh24:mi:ss') event_ts, 'login' event_type FROM dual UNION ALL
SELECT 'xyz' usr, to_date('2015-12-31 18:00:00', 'yyyy-mm-dd hh24:mi:ss') event_ts, 'login' event_type FROM dual UNION ALL
SELECT 'xyz' usr, to_date('2016-01-01 08:00:00', 'yyyy-mm-dd hh24:mi:ss') event_ts, 'Logout' event_type FROM dual UNION ALL
SELECT 'def' usr, to_date('2016-01-01 08:00:00', 'yyyy-mm-dd hh24:mi:ss') event_ts, 'Logout' event_type FROM dual UNION ALL
SELECT 'def' usr, to_date('2016-01-01 08:15:00', 'yyyy-mm-dd hh24:mi:ss') event_ts, 'Logout' event_type FROM dual)
SELECT usr,
event_ts,
event_type,
SUM(counter) OVER (PARTITION BY usr ORDER BY event_ts) session_id
FROM (SELECT usr,
event_ts,
event_type,
CASE WHEN LAG(event_type, 1, 'Logout') OVER (PARTITION BY usr ORDER BY event_ts) = 'Logout' THEN 1
WHEN event_type = 'Logout' THEN 0
WHEN event_ts - LAG(event_ts) OVER (PARTITION BY usr ORDER BY event_ts) > 1/24 THEN 1
WHEN event_type = 'login' THEN 1
ELSE 0
END counter
FROM EVENTS);
USR EVENT_TS EVENT_TYPE SESSION_ID
--- ------------------- ---------- ----------
abc 2016-01-01 08:00:00 login 1
abc 2016-01-01 08:25:00 Stuff 1
abc 2016-01-01 10:00:00 Stuff 2
abc 2016-01-01 14:00:00 login 3
def 2016-01-01 08:00:00 Logout 1
def 2016-01-01 08:15:00 Logout 2
xyz 2015-12-31 18:00:00 login 1
xyz 2016-01-01 08:00:00 Logout 1
Diese Lösung beruht auf dem logischen Kurzschluss, der im CASE-Ausdruck stattfindet, und auf der Tatsache, dass event_type nicht null ist. Es wird auch davon ausgegangen, dass mehrere Abmeldungen hintereinander als separate Sitzungen gezählt werden:
- Wenn die vorherige Zeile eine Logout-Zeile war (und wenn es keine vorherige Zeile gibt – d. h. für die erste Zeile im Satz – behandeln Sie es so, als ob eine Logout-Zeile vorhanden wäre), möchten wir den Zähler um eins erhöhen. (Abmeldungen beenden die Sitzung, sodass wir nach einer Abmeldung immer eine neue Sitzung haben.)
- Wenn die aktuelle Zeile ein Logout ist, dann beendet dies die bestehende Sitzung. Daher sollte der Zähler nicht erhöht werden.
- Wenn die Zeit der aktuellen Zeile mehr als eine Stunde von der vorherigen Zeile entfernt ist, erhöhen Sie den Zähler um eins.
- Wenn die aktuelle Zeile eine Login-Zeile ist, dann ist es eine neue Sitzung, also erhöhen Sie den Zähler um eins.
- In allen anderen Fällen erhöhen wir den Zähler nicht.
Sobald wir das getan haben, müssen wir nur noch eine laufende Summe auf dem Zähler erstellen.