Используя ваши примеры данных, что-то вроде этого может работать. См комментарии в коде.
SQL> with
2 right_now (datum) as
3 -- it is used instead of SYSDATE, because of sample data
4 (select to_date('02.04.2020 14:12', 'dd.mm.yyyy hh24:Mi') from dual),
5 test (userid, logdate) as
6 (select 1, to_date('02.04.2020 14:12', 'dd.mm.yyyy hh24:mi') from dual union all
7 select 1, to_date('02.04.2020 14:00', 'dd.mm.yyyy hh24:mi') from dual union all
8 select 2, to_date('02.04.2020 13:50', 'dd.mm.yyyy hh24:mi') from dual union all
9 select 1, to_date('02.04.2020 13:10', 'dd.mm.yyyy hh24:mi') from dual union all
10 select 2, to_date('02.04.2020 13:11', 'dd.mm.yyyy hh24:mi') from dual union all
11 select 3, to_date('02.04.2020 13:12', 'dd.mm.yyyy hh24:mi') from dual union all
12 select 1, to_date('02.04.2020 10:12', 'dd.mm.yyyy hh24:mi') from dual union all
13 select 4, to_date('02.04.2020 10:14', 'dd.mm.yyyy hh24:mi') from dual
14 ),
15 calendar as
16 -- in real situation, you'd use SYSDATE instead of DATUM and DUAL instead of RIGHT_NOW
17 (select r.datum - (level - 1) / 24 datum,
18 lag(r.datum - (level - 1) / 24) over (order by level desc) prev_datum
19 from right_now r
20 connect by level <= 8
21 )
22 select count(distinct t.userid) distinct_users,
23 to_char(c.prev_datum, 'hh24:mi') ||' - '||
24 to_char(c.datum , 'hh24:mi') period
25 from calendar c left join test t on t.logdate between c.prev_datum and c.datum
26 where c.prev_datum is not null
27 group by to_char(c.prev_datum, 'hh24:mi') ||' - '||
28 to_char(c.datum , 'hh24:mi')
29 order by 2;
DISTINCT_USERS PERIOD
-------------- -------------
0 07:12 - 08:12
0 08:12 - 09:12
1 09:12 - 10:12
2 10:12 - 11:12
0 11:12 - 12:12
3 12:12 - 13:12
3 13:12 - 14:12
7 rows selected.
SQL>