Search code examples
oraclesubsethierarchical-query

Oracle hierarchical query with hierarchy applied to subsets


I am creating customized tool for DB lock monitoring for Oracle SE. Basically i have scheduler job which takes snapshot of gv$session info every 10 seconds and stores it in custom made ashstat_data table.

And now i am trying to create a view to list session blocking chains in hierarchical format for convenience. The view is based on hierarchical select which shows blocked sessions as children under blocking session parent:

with l_snap as
(select * 
from ashstat_data
where sample_time>sysdate - interval '10' second )
SELECT 
s.sample_time,
' '||LPAD('-', (LEVEL-1), '-' ) || SESSION_ID asid,
s.wait_class,
s.event,
s.LOGON_TIME,
s.sql_id
FROM l_snap s
natural join dba_users d
inner join dba_objects do on s.LOCKED_OBJECT_ID=do.object_id
WHERE s.SESSION_ID IN 
(SELECT blocking_session FROM l_snap where blocking_session is not null)
OR s.blocking_session IS NOT NULL
CONNECT BY PRIOR 
 s.SESSION_ID=s.blocking_session
START WITH s.blocking_session IS NULL
;

And so far it works just perfect only if i'm selecting data from a single snapshot (where sample_time>sysdate - interval '10' second):

SAMPLE_TIME              ASID   WAIT_CLASS  LOGON_TIME          SQL_ID
5/9/2018 16:13:08.173    302    Idle        05/09/2018 11:33:57 
5/9/2018 16:13:08.173    -594   Application 05/09/2018 11:34:01 0wvbggc3p3swx
5/9/2018 16:13:08.173    -646   Application 05/09/2018 11:34:07 0wvbggc3p3swx

But if i select records from multiple snapshots (ie. (where sample_time>sysdate - interval '12' second) it arranges them based on connect by condition:

SAMPLE_TIME              ASID   WAIT_CLASS  LOGON_TIME          SQL_ID
5/9/2018 16:17:18.166    302    Idle        05/09/2018 11:33:57 
5/9/2018 16:17:18.166    -594   Application 05/09/2018 11:34:01 0wvbggc3p3swx
5/9/2018 16:17:08.170    -594   Application 05/09/2018 11:34:01 0wvbggc3p3swx
5/9/2018 16:17:18.166    -646   Application 05/09/2018 11:34:07 0wvbggc3p3swx
5/9/2018 16:17:08.170    -646   Application 05/09/2018 11:34:07 0wvbggc3p3swx
5/9/2018 16:17:08.170    302    Idle        05/09/2018 11:33:57 
5/9/2018 16:17:18.166    -594   Application 05/09/2018 11:34:01 0wvbggc3p3swx
5/9/2018 16:17:08.170    -594   Application 05/09/2018 11:34:01 0wvbggc3p3swx
5/9/2018 16:17:18.166    -646   Application 05/09/2018 11:34:07 0wvbggc3p3swx
5/9/2018 16:17:08.170    -646   Application 05/09/2018 11:34:07 0wvbggc3p3swx

What i would like to achieve is that blocking chain is represented for each snapshot individually so the last select result would look like this for the whole collected data:

SAMPLE_TIME              ASID   WAIT_CLASS  LOGON_TIME          SQL_ID
5/9/2018 16:17:18.166    302    Idle        05/09/2018 11:33:57 
5/9/2018 16:17:18.166    -594   Application 05/09/2018 11:34:01 0wvbggc3p3swx
5/9/2018 16:17:18.166    -646   Application 05/09/2018 11:34:07 0wvbggc3p3swx
5/9/2018 16:17:08.170    302    Idle        05/09/2018 11:33:57 
5/9/2018 16:17:08.170    -594   Application 05/09/2018 11:34:01 0wvbggc3p3swx
5/9/2018 16:17:08.170    -646   Application 05/09/2018 11:34:07 0wvbggc3p3swx

Solution

  • You probably need to add the sample_time to your CONNECT BY clause:

    with l_snap as
    (select * 
    from ashstat_data
    where sample_time>sysdate - interval '10' second )
    SELECT 
    s.sample_time,
    ' '||LPAD('-', (LEVEL-1), '-' ) || SESSION_ID asid,
    s.wait_class,
    s.event,
    s.LOGON_TIME,
    s.sql_id
    FROM l_snap s
    natural join dba_users d
    inner join dba_objects do on s.LOCKED_OBJECT_ID=do.object_id
    WHERE s.SESSION_ID IN 
    (SELECT blocking_session FROM l_snap where blocking_session is not null)
    OR s.blocking_session IS NOT NULL
    CONNECT BY PRIOR 
     s.SESSION_ID=s.blocking_session
     and prior s.sample_time = s.sample_time
    START WITH s.blocking_session IS NULL
    ;