Hierarchical Data

Connecting Rows

Say we wanted to take the following directory names from a file system and store them in a database table.


/nls
/nls/demo
/nls/mesg
/server
/server/bin
/server/config
/server/config/log
/server/ctx
/server/ctx/admin
/server/ctx/data
/server/ctx/data/delx
/server/ctx/data/enlx
/server/ctx/data/eslx
/server/ctx/mig
 

To do this we could use a table with a KEY column, which holds the directory name, and a PARENT_KEY column, which connects the directory to its parent directory. (Directory names like these would not typically be used as primary keys. We are bending the rules here for illustrative purposes.)

select * from t ;
 
KEY        PARENT_KEY NAME
---------- ---------- ----------
nls        (null)     NLS
demo       nls        DATA
mesg       nls        DEMO
server     (null)     SERVER
bin        server     BIN
config     server     CONFIG
log        config     LOG
ctx        server     CTX
admin      ctx        ADMIN
data       ctx        DATA
delx       data       DELX
enlx       data       ENLX
eslx       data       ESLX
mig        ctx        MESG
 

To connect and order the data in this table using the PARENT_KEY hierarchy we can create a Hierarchical Query using the START WITH and CONNECT BY clauses of the SELECT command. START WITH identifies the topmost rows in the hierarchy. CONNECT BY identifies all subsequent rows in the hierarchy.

The following snippet returns rows sorted hierarchically, starting from the root rows (those with no parents) on down through to the leaf rows (those with no children).

select
  key ,
  level
from
  t
START WITH
  parent_key is null
CONNECT BY
  parent_key = prior key
;
 
KEY         LEVEL
---------- ------
nls             1
demo            2
mesg            2
server          1
bin             2
config          2
log             3
ctx             2
admin           3
data            3
delx            4
enlx            4
eslx            4
mig             3
 

The LEVEL pseudocolumn in the previous result indicates which level in the hierarchy each row is at. The topmost level is assigned a LEVEL of 1. To better illustrate hierarchical relationships the LEVEL column is commonly used to indent selected values, like this.

select
  lpad( ' ', level-1 ) || key as key_indented ,
  level
from
  t
START WITH
  parent_key is null
CONNECT BY
  parent_key = prior key
;
 
KEY_INDENTED     LEVEL
--------------- ------
nls                  1
 demo                2
 mesg                2
server               1
 bin                 2
 config              2
  log                3
 ctx                 2
  admin              3
  data               3
   delx              4
   enlx              4
   eslx              4
  mig                3
 

The PRIOR operator in hierarchical queries gives us access to column information from the parent of the current row. It can be used outside the CONNECT BY clause if required.

select
  lpad( ' ', level-1 ) || key as key_indented ,
  PRIOR key                   as prior_key   ,
  PRIOR name                  as prior_name
from
  t
start with
  parent_key is null
connect by
  parent_key = prior key
;
 
KEY_INDENTED    PRIOR_KEY  PRIOR_NAME
--------------- ---------- ----------
nls             (null)     (null)
 demo           nls        NLS
 mesg           nls        NLS
server          (null)     (null)
 bin            server     SERVER
 config         server     SERVER
  log           config     CONFIG
 ctx            server     SERVER
  admin         ctx        CTX
  data          ctx        CTX
   delx         data       DATA
   enlx         data       DATA
   eslx         data       DATA
  mig           ctx        CTX
 

Changing Direction

To traverse the tree in the opposite direction, from leaf to root, simply choose a leaf row as the starting point and apply the PRIOR operator to the PARENT_KEY column instead of the KEY column.

select
  lpad( ' ', level-1 ) || key as key_indented ,
  level
from
  t
START WITH
  KEY = 'delx'
connect by
  key = PRIOR PARENT_KEY
;
 
KEY_INDENTED     LEVEL
--------------- ------
delx                 1
 data                2
  ctx                3
   server            4
 

Gotchas

CONNECT BY conditions are not applied to rows in level 1 of the hierarchy. In the following snippet note how the KEY <> 'delx' condition did not filter out the row with a KEY value of 'delx'.

select
  lpad( ' ', level-1 ) || key as key_indented ,
  level
from
  t
start with
  key = 'delx'
connect by
  key = PRIOR PARENT_KEY and
  KEY <> 'delx'
;
 
KEY_INDENTED     LEVEL
--------------- ------
delx                 1
 data                2
  ctx                3
   server            4
 



Linking to SQL Snippets ™

To link to this page in Oracle Technology Network Forums or OraFAQ Forums cut and paste this code.

  • [url=http://www.sqlsnippets.com/en/topic-10919.html]SQL Snippets: Hierarchical Data - Connecting Rows[/url]

To link to this page in HTML documents or Blogger comments cut and paste this code.

  • <a href="http://www.sqlsnippets.com/en/topic-10919.html">SQL Snippets: Hierarchical Data - Connecting Rows</a>

To link to this page in other web sites use the following values.

  • Link Text : SQL Snippets: Hierarchical Data - Connecting Rows
  • URL (href): http://www.sqlsnippets.com/en/topic-10919.html