Saya pikir ini akan berhasil:
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
Solusi ini bergantung pada hubung singkat logika yang terjadi dalam ekspresi CASE dan fakta bahwa event_type tidak nol. Ini juga mengasumsikan bahwa beberapa logout berturut-turut dihitung sebagai sesi terpisah:
- Jika baris sebelumnya adalah baris keluar (dan jika tidak ada baris sebelumnya - yaitu untuk baris pertama dalam kumpulan - perlakukan seolah-olah ada baris keluar), kami ingin menambah penghitung satu per satu. (Logout mengakhiri sesi, jadi kami selalu memiliki sesi baru setelah logout.)
- Jika baris saat ini adalah logout, maka sesi yang ada akan dihentikan. Oleh karena itu, penghitung tidak boleh ditingkatkan.
- Jika waktu baris saat ini lebih besar dari satu jam dari baris sebelumnya, tambah penghitung satu per satu.
- Jika baris saat ini adalah baris login, maka ini adalah sesi baru, jadi tambah penghitungnya satu per satu.
- Untuk kasus lain, kami tidak menambah penghitung.
Setelah kami selesai melakukannya, tinggal melakukan total lari di konter.